diff --git a/.changelog/14285.txt b/.changelog/14285.txt new file mode 100644 index 000000000..6e82e5458 --- /dev/null +++ b/.changelog/14285.txt @@ -0,0 +1,3 @@ +```release-note:feature +connect: Server address changes are streamed to peers +``` \ No newline at end of file diff --git a/.changelog/14474.txt b/.changelog/14474.txt new file mode 100644 index 000000000..fcc326547 --- /dev/null +++ b/.changelog/14474.txt @@ -0,0 +1,3 @@ +```release-note:feature +http: Add new `get-or-empty` operation to the txn api. Refer to the [API docs](https://www.consul.io/api-docs/txn#kv-operations) for more information. +``` \ No newline at end of file diff --git a/.changelog/14495.txt b/.changelog/14495.txt new file mode 100644 index 000000000..e002bbbdc --- /dev/null +++ b/.changelog/14495.txt @@ -0,0 +1,3 @@ +```release-note:feature +ui: Detect a TokenSecretID cookie and passthrough to localStorage +``` diff --git a/.changelog/14521.txt b/.changelog/14521.txt new file mode 100644 index 000000000..8dc0de40f --- /dev/null +++ b/.changelog/14521.txt @@ -0,0 +1,3 @@ +```release-note:improvement +ui: Reuse connections for requests to /v1/internal/ui/metrics-proxy/ +``` diff --git a/agent/agent.go b/agent/agent.go index 8a263647e..1bcb6c148 100644 --- a/agent/agent.go +++ b/agent/agent.go @@ -939,8 +939,9 @@ func (a *Agent) listenHTTP() ([]apiServer, error) { } srv := &HTTPHandlers{ - agent: a, - denylist: NewDenylist(a.config.HTTPBlockEndpoints), + agent: a, + denylist: NewDenylist(a.config.HTTPBlockEndpoints), + proxyTransport: http.DefaultTransport, } a.configReloaders = append(a.configReloaders, srv.ReloadConfig) a.httpHandlers = srv @@ -2104,6 +2105,21 @@ func (a *Agent) AddService(req AddServiceRequest) error { // addServiceLocked adds a service entry to the service manager if enabled, or directly // to the local state if it is not. This function assumes the state lock is already held. func (a *Agent) addServiceLocked(req addServiceLockedRequest) error { + // Must auto-assign the port and default checks (if needed) here to avoid race collisions. + if req.Service.LocallyRegisteredAsSidecar { + if req.Service.Port < 1 { + port, err := a.sidecarPortFromServiceIDLocked(req.Service.CompoundServiceID()) + if err != nil { + return err + } + req.Service.Port = port + } + // Setup default check if none given. + if len(req.chkTypes) < 1 { + req.chkTypes = sidecarDefaultChecks(req.Service.ID, req.Service.Address, req.Service.Proxy.LocalServiceAddress, req.Service.Port) + } + } + req.Service.EnterpriseMeta.Normalize() if err := a.validateService(req.Service, req.chkTypes); err != nil { @@ -3368,7 +3384,7 @@ func (a *Agent) loadServices(conf *config.RuntimeConfig, snap map[structs.CheckI } // Grab and validate sidecar if there is one too - sidecar, sidecarChecks, sidecarToken, err := a.sidecarServiceFromNodeService(ns, service.Token) + sidecar, sidecarChecks, sidecarToken, err := sidecarServiceFromNodeService(ns, service.Token) if err != nil { return fmt.Errorf("Failed to validate sidecar for service %q: %v", service.Name, err) } @@ -4268,7 +4284,10 @@ func (a *Agent) proxyDataSources() proxycfg.DataSources { sources.Health = proxycfgglue.ServerHealth(deps, proxycfgglue.ClientHealth(a.rpcClientHealth)) sources.Intentions = proxycfgglue.ServerIntentions(deps) sources.IntentionUpstreams = proxycfgglue.ServerIntentionUpstreams(deps) + sources.IntentionUpstreamsDestination = proxycfgglue.ServerIntentionUpstreamsDestination(deps) + sources.InternalServiceDump = proxycfgglue.ServerInternalServiceDump(deps, proxycfgglue.CacheInternalServiceDump(a.cache)) sources.PeeredUpstreams = proxycfgglue.ServerPeeredUpstreams(deps) + sources.ResolvedServiceConfig = proxycfgglue.ServerResolvedServiceConfig(deps, proxycfgglue.CacheResolvedServiceConfig(a.cache)) sources.ServiceList = proxycfgglue.ServerServiceList(deps, proxycfgglue.CacheServiceList(a.cache)) sources.TrustBundle = proxycfgglue.ServerTrustBundle(deps) sources.TrustBundleList = proxycfgglue.ServerTrustBundleList(deps) diff --git a/agent/agent_endpoint.go b/agent/agent_endpoint.go index b2d68e304..e758d4bbf 100644 --- a/agent/agent_endpoint.go +++ b/agent/agent_endpoint.go @@ -1159,7 +1159,7 @@ func (s *HTTPHandlers) AgentRegisterService(resp http.ResponseWriter, req *http. } // See if we have a sidecar to register too - sidecar, sidecarChecks, sidecarToken, err := s.agent.sidecarServiceFromNodeService(ns, token) + sidecar, sidecarChecks, sidecarToken, err := sidecarServiceFromNodeService(ns, token) if err != nil { return nil, HTTPError{StatusCode: http.StatusBadRequest, Reason: fmt.Sprintf("Invalid SidecarService: %s", err)} } diff --git a/agent/agent_test.go b/agent/agent_test.go index 8bae81ce4..b0da44d6e 100644 --- a/agent/agent_test.go +++ b/agent/agent_test.go @@ -2786,7 +2786,7 @@ func TestAgent_DeregisterPersistedSidecarAfterRestart(t *testing.T) { }, } - connectSrv, _, _, err := a.sidecarServiceFromNodeService(srv, "") + connectSrv, _, _, err := sidecarServiceFromNodeService(srv, "") require.NoError(t, err) // First persist the check @@ -2959,11 +2959,24 @@ func testAgent_loadServices_sidecar(t *testing.T, extraHCL string) { if token := a.State.ServiceToken(structs.NewServiceID("rabbitmq", nil)); token != "abc123" { t.Fatalf("bad: %s", token) } - requireServiceExists(t, a, "rabbitmq-sidecar-proxy") + sidecarSvc := requireServiceExists(t, a, "rabbitmq-sidecar-proxy") if token := a.State.ServiceToken(structs.NewServiceID("rabbitmq-sidecar-proxy", nil)); token != "abc123" { t.Fatalf("bad: %s", token) } + // Verify default checks have been added + wantChecks := sidecarDefaultChecks(sidecarSvc.ID, sidecarSvc.Address, sidecarSvc.Proxy.LocalServiceAddress, sidecarSvc.Port) + gotChecks := a.State.ChecksForService(sidecarSvc.CompoundServiceID(), true) + gotChkNames := make(map[string]types.CheckID) + for _, check := range gotChecks { + requireCheckExists(t, a, check.CheckID) + gotChkNames[check.Name] = check.CheckID + } + for _, check := range wantChecks { + chkName := check.Name + require.NotNil(t, gotChkNames[chkName]) + } + // Sanity check rabbitmq service should NOT have sidecar info in state since // it's done it's job and should be a registration syntax sugar only. assert.Nil(t, svc.Connect.SidecarService) diff --git a/agent/configentry/resolve.go b/agent/configentry/resolve.go new file mode 100644 index 000000000..ec85f34d2 --- /dev/null +++ b/agent/configentry/resolve.go @@ -0,0 +1,229 @@ +package configentry + +import ( + "fmt" + + "github.com/hashicorp/go-hclog" + "github.com/mitchellh/copystructure" + + "github.com/hashicorp/consul/agent/structs" +) + +func ComputeResolvedServiceConfig( + args *structs.ServiceConfigRequest, + upstreamIDs []structs.ServiceID, + legacyUpstreams bool, + entries *ResolvedServiceConfigSet, + logger hclog.Logger, +) (*structs.ServiceConfigResponse, error) { + var thisReply structs.ServiceConfigResponse + + thisReply.MeshGateway.Mode = structs.MeshGatewayModeDefault + + // TODO(freddy) Refactor this into smaller set of state store functions + // Pass the WatchSet to both the service and proxy config lookups. If either is updated during the + // blocking query, this function will be rerun and these state store lookups will both be current. + // We use the default enterprise meta to look up the global proxy defaults because they are not namespaced. + var proxyConfGlobalProtocol string + proxyConf := entries.GetProxyDefaults(args.PartitionOrDefault()) + if proxyConf != nil { + // Apply the proxy defaults to the sidecar's proxy config + mapCopy, err := copystructure.Copy(proxyConf.Config) + if err != nil { + return nil, fmt.Errorf("failed to copy global proxy-defaults: %v", err) + } + thisReply.ProxyConfig = mapCopy.(map[string]interface{}) + thisReply.Mode = proxyConf.Mode + thisReply.TransparentProxy = proxyConf.TransparentProxy + thisReply.MeshGateway = proxyConf.MeshGateway + thisReply.Expose = proxyConf.Expose + + // Extract the global protocol from proxyConf for upstream configs. + rawProtocol := proxyConf.Config["protocol"] + if rawProtocol != nil { + var ok bool + proxyConfGlobalProtocol, ok = rawProtocol.(string) + if !ok { + return nil, fmt.Errorf("invalid protocol type %T", rawProtocol) + } + } + } + + serviceConf := entries.GetServiceDefaults( + structs.NewServiceID(args.Name, &args.EnterpriseMeta), + ) + if serviceConf != nil { + if serviceConf.Expose.Checks { + thisReply.Expose.Checks = true + } + if len(serviceConf.Expose.Paths) >= 1 { + thisReply.Expose.Paths = serviceConf.Expose.Paths + } + if serviceConf.MeshGateway.Mode != structs.MeshGatewayModeDefault { + thisReply.MeshGateway.Mode = serviceConf.MeshGateway.Mode + } + if serviceConf.Protocol != "" { + if thisReply.ProxyConfig == nil { + thisReply.ProxyConfig = make(map[string]interface{}) + } + thisReply.ProxyConfig["protocol"] = serviceConf.Protocol + } + if serviceConf.TransparentProxy.OutboundListenerPort != 0 { + thisReply.TransparentProxy.OutboundListenerPort = serviceConf.TransparentProxy.OutboundListenerPort + } + if serviceConf.TransparentProxy.DialedDirectly { + thisReply.TransparentProxy.DialedDirectly = serviceConf.TransparentProxy.DialedDirectly + } + if serviceConf.Mode != structs.ProxyModeDefault { + thisReply.Mode = serviceConf.Mode + } + if serviceConf.Destination != nil { + thisReply.Destination = *serviceConf.Destination + } + + if serviceConf.MaxInboundConnections > 0 { + if thisReply.ProxyConfig == nil { + thisReply.ProxyConfig = map[string]interface{}{} + } + thisReply.ProxyConfig["max_inbound_connections"] = serviceConf.MaxInboundConnections + } + + thisReply.Meta = serviceConf.Meta + } + + // First collect all upstreams into a set of seen upstreams. + // Upstreams can come from: + // - Explicitly from proxy registrations, and therefore as an argument to this RPC endpoint + // - Implicitly from centralized upstream config in service-defaults + seenUpstreams := map[structs.ServiceID]struct{}{} + + var ( + noUpstreamArgs = len(upstreamIDs) == 0 && len(args.Upstreams) == 0 + + // Check the args and the resolved value. If it was exclusively set via a config entry, then args.Mode + // will never be transparent because the service config request does not use the resolved value. + tproxy = args.Mode == structs.ProxyModeTransparent || thisReply.Mode == structs.ProxyModeTransparent + ) + + // The upstreams passed as arguments to this endpoint are the upstreams explicitly defined in a proxy registration. + // If no upstreams were passed, then we should only return the resolved config if the proxy is in transparent mode. + // Otherwise we would return a resolved upstream config to a proxy with no configured upstreams. + if noUpstreamArgs && !tproxy { + return &thisReply, nil + } + + // First store all upstreams that were provided in the request + for _, sid := range upstreamIDs { + if _, ok := seenUpstreams[sid]; !ok { + seenUpstreams[sid] = struct{}{} + } + } + + // Then store upstreams inferred from service-defaults and mapify the overrides. + var ( + upstreamConfigs = make(map[structs.ServiceID]*structs.UpstreamConfig) + upstreamDefaults *structs.UpstreamConfig + // usConfigs stores the opaque config map for each upstream and is keyed on the upstream's ID. + usConfigs = make(map[structs.ServiceID]map[string]interface{}) + ) + if serviceConf != nil && serviceConf.UpstreamConfig != nil { + for i, override := range serviceConf.UpstreamConfig.Overrides { + if override.Name == "" { + logger.Warn( + "Skipping UpstreamConfig.Overrides entry without a required name field", + "entryIndex", i, + "kind", serviceConf.GetKind(), + "name", serviceConf.GetName(), + "namespace", serviceConf.GetEnterpriseMeta().NamespaceOrEmpty(), + ) + continue // skip this impossible condition + } + seenUpstreams[override.ServiceID()] = struct{}{} + upstreamConfigs[override.ServiceID()] = override + } + if serviceConf.UpstreamConfig.Defaults != nil { + upstreamDefaults = serviceConf.UpstreamConfig.Defaults + + // Store the upstream defaults under a wildcard key so that they can be applied to + // upstreams that are inferred from intentions and do not have explicit upstream configuration. + cfgMap := make(map[string]interface{}) + upstreamDefaults.MergeInto(cfgMap) + + wildcard := structs.NewServiceID(structs.WildcardSpecifier, args.WithWildcardNamespace()) + usConfigs[wildcard] = cfgMap + } + } + + for upstream := range seenUpstreams { + resolvedCfg := make(map[string]interface{}) + + // The protocol of an upstream is resolved in this order: + // 1. Default protocol from proxy-defaults (how all services should be addressed) + // 2. Protocol for upstream service defined in its service-defaults (how the upstream wants to be addressed) + // 3. Protocol defined for the upstream in the service-defaults.(upstream_config.defaults|upstream_config.overrides) of the downstream + // (how the downstream wants to address it) + protocol := proxyConfGlobalProtocol + + upstreamSvcDefaults := entries.GetServiceDefaults( + structs.NewServiceID(upstream.ID, &upstream.EnterpriseMeta), + ) + if upstreamSvcDefaults != nil { + if upstreamSvcDefaults.Protocol != "" { + protocol = upstreamSvcDefaults.Protocol + } + } + + if protocol != "" { + resolvedCfg["protocol"] = protocol + } + + // Merge centralized defaults for all upstreams before configuration for specific upstreams + if upstreamDefaults != nil { + upstreamDefaults.MergeInto(resolvedCfg) + } + + // The MeshGateway value from the proxy registration overrides the one from upstream_defaults + // because it is specific to the proxy instance. + // + // The goal is to flatten the mesh gateway mode in this order: + // 0. Value from centralized upstream_defaults + // 1. Value from local proxy registration + // 2. Value from centralized upstream_config + // 3. Value from local upstream definition. This last step is done in the client's service manager. + if !args.MeshGateway.IsZero() { + resolvedCfg["mesh_gateway"] = args.MeshGateway + } + + if upstreamConfigs[upstream] != nil { + upstreamConfigs[upstream].MergeInto(resolvedCfg) + } + + if len(resolvedCfg) > 0 { + usConfigs[upstream] = resolvedCfg + } + } + + // don't allocate the slices just to not fill them + if len(usConfigs) == 0 { + return &thisReply, nil + } + + if legacyUpstreams { + // For legacy upstreams we return a map that is only keyed on the string ID, since they precede namespaces + thisReply.UpstreamConfigs = make(map[string]map[string]interface{}) + + for us, conf := range usConfigs { + thisReply.UpstreamConfigs[us.ID] = conf + } + + } else { + thisReply.UpstreamIDConfigs = make(structs.OpaqueUpstreamConfigs, 0, len(usConfigs)) + + for us, conf := range usConfigs { + thisReply.UpstreamIDConfigs = append(thisReply.UpstreamIDConfigs, + structs.OpaqueUpstreamConfig{Upstream: us, Config: conf}) + } + } + + return &thisReply, nil +} diff --git a/agent/configentry/resolve_test.go b/agent/configentry/resolve_test.go new file mode 100644 index 000000000..30a03aae6 --- /dev/null +++ b/agent/configentry/resolve_test.go @@ -0,0 +1,56 @@ +package configentry + +import ( + "testing" + + "github.com/stretchr/testify/require" + + "github.com/hashicorp/consul/agent/structs" +) + +func Test_ComputeResolvedServiceConfig(t *testing.T) { + type args struct { + scReq *structs.ServiceConfigRequest + upstreamIDs []structs.ServiceID + entries *ResolvedServiceConfigSet + } + + sid := structs.ServiceID{ + ID: "sid", + EnterpriseMeta: *structs.DefaultEnterpriseMetaInDefaultPartition(), + } + tests := []struct { + name string + args args + want *structs.ServiceConfigResponse + }{ + { + name: "proxy with maxinboundsconnections", + args: args{ + scReq: &structs.ServiceConfigRequest{ + Name: "sid", + }, + entries: &ResolvedServiceConfigSet{ + ServiceDefaults: map[structs.ServiceID]*structs.ServiceConfigEntry{ + sid: { + MaxInboundConnections: 20, + }, + }, + }, + }, + want: &structs.ServiceConfigResponse{ + ProxyConfig: map[string]interface{}{ + "max_inbound_connections": 20, + }, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := ComputeResolvedServiceConfig(tt.args.scReq, tt.args.upstreamIDs, + false, tt.args.entries, nil) + require.NoError(t, err) + require.Equal(t, tt.want, got) + }) + } +} diff --git a/agent/connect/uri.go b/agent/connect/uri.go index 18f888d25..6d64be3b4 100644 --- a/agent/connect/uri.go +++ b/agent/connect/uri.go @@ -24,6 +24,8 @@ var ( `^(?:/ap/([^/]+))?/ns/([^/]+)/dc/([^/]+)/svc/([^/]+)$`) spiffeIDAgentRegexp = regexp.MustCompile( `^(?:/ap/([^/]+))?/agent/client/dc/([^/]+)/id/([^/]+)$`) + spiffeIDServerRegexp = regexp.MustCompile( + `^/agent/server/dc/([^/]+)$`) spiffeIDMeshGatewayRegexp = regexp.MustCompile( `^(?:/ap/([^/]+))?/gateway/mesh/dc/([^/]+)$`) ) @@ -144,6 +146,19 @@ func ParseCertURI(input *url.URL) (CertURI, error) { Partition: ap, Datacenter: dc, }, nil + } else if v := spiffeIDServerRegexp.FindStringSubmatch(path); v != nil { + dc := v[1] + if input.RawPath != "" { + var err error + if dc, err = url.PathUnescape(v[1]); err != nil { + return nil, fmt.Errorf("Invalid datacenter: %s", err) + } + } + + return &SpiffeIDServer{ + Host: input.Host, + Datacenter: dc, + }, nil } // Test for signing ID diff --git a/agent/connect/uri_server.go b/agent/connect/uri_server.go new file mode 100644 index 000000000..3d120b5b9 --- /dev/null +++ b/agent/connect/uri_server.go @@ -0,0 +1,20 @@ +package connect + +import ( + "fmt" + "net/url" +) + +type SpiffeIDServer struct { + Host string + Datacenter string +} + +// URI returns the *url.URL for this SPIFFE ID. +func (id SpiffeIDServer) URI() *url.URL { + var result url.URL + result.Scheme = "spiffe" + result.Host = id.Host + result.Path = fmt.Sprintf("/agent/server/dc/%s", id.Datacenter) + return &result +} diff --git a/agent/connect/uri_signing.go b/agent/connect/uri_signing.go index 926674880..67ef7ecea 100644 --- a/agent/connect/uri_signing.go +++ b/agent/connect/uri_signing.go @@ -54,6 +54,12 @@ func (id SpiffeIDSigning) CanSign(cu CertURI) bool { // worry about Unicode domains if we start allowing customisation beyond the // built-in cluster ids. return strings.ToLower(other.Host) == id.Host() + case *SpiffeIDServer: + // The host component of the service must be an exact match for now under + // ascii case folding (since hostnames are case-insensitive). Later we might + // worry about Unicode domains if we start allowing customisation beyond the + // built-in cluster ids. + return strings.ToLower(other.Host) == id.Host() default: return false } diff --git a/agent/connect/uri_signing_test.go b/agent/connect/uri_signing_test.go index e09ac78c6..6fef1f811 100644 --- a/agent/connect/uri_signing_test.go +++ b/agent/connect/uri_signing_test.go @@ -78,7 +78,7 @@ func TestSpiffeIDSigning_CanSign(t *testing.T) { want: true, }, { - name: "service - good midex case", + name: "service - good mixed case", id: testSigning, input: &SpiffeIDService{Host: strings.ToUpper(TestClusterID) + ".CONsuL", Namespace: "defAUlt", Datacenter: "dc1", Service: "WEB"}, want: true, @@ -102,7 +102,7 @@ func TestSpiffeIDSigning_CanSign(t *testing.T) { want: true, }, { - name: "mesh gateway - good midex case", + name: "mesh gateway - good mixed case", id: testSigning, input: &SpiffeIDMeshGateway{Host: strings.ToUpper(TestClusterID) + ".CONsuL", Datacenter: "dc1"}, want: true, @@ -119,6 +119,30 @@ func TestSpiffeIDSigning_CanSign(t *testing.T) { input: &SpiffeIDMeshGateway{Host: TestClusterID + ".fake", Datacenter: "dc1"}, want: false, }, + { + name: "server - good", + id: testSigning, + input: &SpiffeIDServer{Host: TestClusterID + ".consul", Datacenter: "dc1"}, + want: true, + }, + { + name: "server - good mixed case", + id: testSigning, + input: &SpiffeIDServer{Host: strings.ToUpper(TestClusterID) + ".CONsuL", Datacenter: "dc1"}, + want: true, + }, + { + name: "server - different cluster", + id: testSigning, + input: &SpiffeIDServer{Host: "55555555-4444-3333-2222-111111111111.consul", Datacenter: "dc1"}, + want: false, + }, + { + name: "server - different TLD", + id: testSigning, + input: &SpiffeIDServer{Host: TestClusterID + ".fake", Datacenter: "dc1"}, + want: false, + }, } for _, tt := range tests { diff --git a/agent/connect/uri_test.go b/agent/connect/uri_test.go index 9c2849c4c..1cc3f7a1f 100644 --- a/agent/connect/uri_test.go +++ b/agent/connect/uri_test.go @@ -19,109 +19,118 @@ func TestParseCertURIFromString(t *testing.T) { ParseError string }{ { - "invalid scheme", - "http://google.com/", - nil, - "scheme", + Name: "invalid scheme", + URI: "http://google.com/", + Struct: nil, + ParseError: "scheme", }, { - "basic service ID", - "spiffe://1234.consul/ns/default/dc/dc01/svc/web", - &SpiffeIDService{ + Name: "basic service ID", + URI: "spiffe://1234.consul/ns/default/dc/dc01/svc/web", + Struct: &SpiffeIDService{ Host: "1234.consul", Partition: defaultEntMeta.PartitionOrDefault(), Namespace: "default", Datacenter: "dc01", Service: "web", }, - "", + ParseError: "", }, { - "basic service ID with partition", - "spiffe://1234.consul/ap/bizdev/ns/default/dc/dc01/svc/web", - &SpiffeIDService{ + Name: "basic service ID with partition", + URI: "spiffe://1234.consul/ap/bizdev/ns/default/dc/dc01/svc/web", + Struct: &SpiffeIDService{ Host: "1234.consul", Partition: "bizdev", Namespace: "default", Datacenter: "dc01", Service: "web", }, - "", + ParseError: "", }, { - "basic agent ID", - "spiffe://1234.consul/agent/client/dc/dc1/id/uuid", - &SpiffeIDAgent{ + Name: "basic agent ID", + URI: "spiffe://1234.consul/agent/client/dc/dc1/id/uuid", + Struct: &SpiffeIDAgent{ Host: "1234.consul", Partition: defaultEntMeta.PartitionOrDefault(), Datacenter: "dc1", Agent: "uuid", }, - "", + ParseError: "", }, { - "basic agent ID with partition", - "spiffe://1234.consul/ap/bizdev/agent/client/dc/dc1/id/uuid", - &SpiffeIDAgent{ + Name: "basic agent ID with partition", + URI: "spiffe://1234.consul/ap/bizdev/agent/client/dc/dc1/id/uuid", + Struct: &SpiffeIDAgent{ Host: "1234.consul", Partition: "bizdev", Datacenter: "dc1", Agent: "uuid", }, - "", + ParseError: "", }, { - "mesh-gateway with no partition", - "spiffe://1234.consul/gateway/mesh/dc/dc1", - &SpiffeIDMeshGateway{ + Name: "basic server", + URI: "spiffe://1234.consul/agent/server/dc/dc1", + Struct: &SpiffeIDServer{ + Host: "1234.consul", + Datacenter: "dc1", + }, + ParseError: "", + }, + { + Name: "mesh-gateway with no partition", + URI: "spiffe://1234.consul/gateway/mesh/dc/dc1", + Struct: &SpiffeIDMeshGateway{ Host: "1234.consul", Partition: "default", Datacenter: "dc1", }, - "", + ParseError: "", }, { - "mesh-gateway with partition", - "spiffe://1234.consul/ap/bizdev/gateway/mesh/dc/dc1", - &SpiffeIDMeshGateway{ + Name: "mesh-gateway with partition", + URI: "spiffe://1234.consul/ap/bizdev/gateway/mesh/dc/dc1", + Struct: &SpiffeIDMeshGateway{ Host: "1234.consul", Partition: "bizdev", Datacenter: "dc1", }, - "", + ParseError: "", }, { - "service with URL-encoded values", - "spiffe://1234.consul/ns/foo%2Fbar/dc/bar%2Fbaz/svc/baz%2Fqux", - &SpiffeIDService{ + Name: "service with URL-encoded values", + URI: "spiffe://1234.consul/ns/foo%2Fbar/dc/bar%2Fbaz/svc/baz%2Fqux", + Struct: &SpiffeIDService{ Host: "1234.consul", Partition: defaultEntMeta.PartitionOrDefault(), Namespace: "foo/bar", Datacenter: "bar/baz", Service: "baz/qux", }, - "", + ParseError: "", }, { - "service with URL-encoded values with partition", - "spiffe://1234.consul/ap/biz%2Fdev/ns/foo%2Fbar/dc/bar%2Fbaz/svc/baz%2Fqux", - &SpiffeIDService{ + Name: "service with URL-encoded values with partition", + URI: "spiffe://1234.consul/ap/biz%2Fdev/ns/foo%2Fbar/dc/bar%2Fbaz/svc/baz%2Fqux", + Struct: &SpiffeIDService{ Host: "1234.consul", Partition: "biz/dev", Namespace: "foo/bar", Datacenter: "bar/baz", Service: "baz/qux", }, - "", + ParseError: "", }, { - "signing ID", - "spiffe://1234.consul", - &SpiffeIDSigning{ + Name: "signing ID", + URI: "spiffe://1234.consul", + Struct: &SpiffeIDSigning{ ClusterID: "1234", Domain: "consul", }, - "", + ParseError: "", }, } @@ -139,3 +148,12 @@ func TestParseCertURIFromString(t *testing.T) { }) } } + +func TestSpiffeIDServer_URI(t *testing.T) { + srv := &SpiffeIDServer{ + Host: "1234.consul", + Datacenter: "dc1", + } + + require.Equal(t, "spiffe://1234.consul/agent/server/dc/dc1", srv.URI().String()) +} diff --git a/agent/consul/autopilotevents/mock_StateStore_test.go b/agent/consul/autopilotevents/mock_StateStore_test.go index 0262b410a..dd048e58e 100644 --- a/agent/consul/autopilotevents/mock_StateStore_test.go +++ b/agent/consul/autopilotevents/mock_StateStore_test.go @@ -4,6 +4,8 @@ package autopilotevents import ( acl "github.com/hashicorp/consul/acl" + memdb "github.com/hashicorp/go-memdb" + mock "github.com/stretchr/testify/mock" structs "github.com/hashicorp/consul/agent/structs" @@ -48,6 +50,36 @@ func (_m *MockStateStore) GetNodeID(_a0 types.NodeID, _a1 *acl.EnterpriseMeta, _ return r0, r1, r2 } +// NodeService provides a mock function with given fields: ws, nodeName, serviceID, entMeta, peerName +func (_m *MockStateStore) NodeService(ws memdb.WatchSet, nodeName string, serviceID string, entMeta *acl.EnterpriseMeta, peerName string) (uint64, *structs.NodeService, error) { + ret := _m.Called(ws, nodeName, serviceID, entMeta, peerName) + + var r0 uint64 + if rf, ok := ret.Get(0).(func(memdb.WatchSet, string, string, *acl.EnterpriseMeta, string) uint64); ok { + r0 = rf(ws, nodeName, serviceID, entMeta, peerName) + } else { + r0 = ret.Get(0).(uint64) + } + + var r1 *structs.NodeService + if rf, ok := ret.Get(1).(func(memdb.WatchSet, string, string, *acl.EnterpriseMeta, string) *structs.NodeService); ok { + r1 = rf(ws, nodeName, serviceID, entMeta, peerName) + } else { + if ret.Get(1) != nil { + r1 = ret.Get(1).(*structs.NodeService) + } + } + + var r2 error + if rf, ok := ret.Get(2).(func(memdb.WatchSet, string, string, *acl.EnterpriseMeta, string) error); ok { + r2 = rf(ws, nodeName, serviceID, entMeta, peerName) + } else { + r2 = ret.Error(2) + } + + return r0, r1, r2 +} + // NewMockStateStore creates a new instance of MockStateStore. It also registers the testing.TB interface on the mock and a cleanup function to assert the mocks expectations. func NewMockStateStore(t testing.TB) *MockStateStore { mock := &MockStateStore{} diff --git a/agent/consul/autopilotevents/ready_servers_events.go b/agent/consul/autopilotevents/ready_servers_events.go index ad3221e9a..cbc981949 100644 --- a/agent/consul/autopilotevents/ready_servers_events.go +++ b/agent/consul/autopilotevents/ready_servers_events.go @@ -4,9 +4,11 @@ import ( "fmt" "net" "sort" + "strconv" "sync" "time" + "github.com/hashicorp/go-memdb" autopilot "github.com/hashicorp/raft-autopilot" "github.com/hashicorp/consul/acl" @@ -26,6 +28,7 @@ type ReadyServerInfo struct { ID string Address string TaggedAddresses map[string]string + ExtGRPCPort int Version string } @@ -122,6 +125,7 @@ func NewReadyServersEventPublisher(config Config) *ReadyServersEventPublisher { //go:generate mockery --name StateStore --inpackage --filename mock_StateStore_test.go type StateStore interface { GetNodeID(types.NodeID, *acl.EnterpriseMeta, string) (uint64, *structs.Node, error) + NodeService(ws memdb.WatchSet, nodeName string, serviceID string, entMeta *acl.EnterpriseMeta, peerName string) (uint64, *structs.NodeService, error) } //go:generate mockery --name Publisher --inpackage --filename mock_Publisher_test.go @@ -226,6 +230,7 @@ func (r *ReadyServersEventPublisher) autopilotStateToReadyServers(state *autopil Address: host, Version: srv.Server.Version, TaggedAddresses: r.getTaggedAddresses(srv), + ExtGRPCPort: r.getGRPCPort(srv), }) } } @@ -254,7 +259,7 @@ func (r *ReadyServersEventPublisher) getTaggedAddresses(srv *autopilot.ServerSta // code and reason about and having those addresses be updated within 30s is good enough. _, node, err := r.GetStore().GetNodeID(types.NodeID(srv.Server.ID), structs.NodeEnterpriseMetaInDefaultPartition(), structs.DefaultPeerKeyword) if err != nil || node == nil { - // no catalog information means we should return a nil addres map + // no catalog information means we should return a nil address map return nil } @@ -276,6 +281,38 @@ func (r *ReadyServersEventPublisher) getTaggedAddresses(srv *autopilot.ServerSta return addrs } +// getGRPCPort will get the external gRPC port for a Consul server. +// Returns 0 if there is none assigned or if an error is encountered. +func (r *ReadyServersEventPublisher) getGRPCPort(srv *autopilot.ServerState) int { + if r.GetStore == nil { + return 0 + } + + _, n, err := r.GetStore().GetNodeID(types.NodeID(srv.Server.ID), structs.NodeEnterpriseMetaInDefaultPartition(), structs.DefaultPeerKeyword) + if err != nil || n == nil { + return 0 + } + + _, ns, err := r.GetStore().NodeService( + nil, + n.Node, + structs.ConsulServiceID, + structs.NodeEnterpriseMetaInDefaultPartition(), + structs.DefaultPeerKeyword, + ) + if err != nil || ns == nil || ns.Meta == nil { + return 0 + } + if str, ok := ns.Meta["grpc_port"]; ok { + grpcPort, err := strconv.Atoi(str) + if err == nil { + return grpcPort + } + } + + return 0 +} + // newReadyServersEvent will create a stream.Event with the provided ready server info. func (r *ReadyServersEventPublisher) newReadyServersEvent(servers EventPayloadReadyServers) stream.Event { now := time.Now() diff --git a/agent/consul/autopilotevents/ready_servers_events_test.go b/agent/consul/autopilotevents/ready_servers_events_test.go index 223292404..0f686fbc5 100644 --- a/agent/consul/autopilotevents/ready_servers_events_test.go +++ b/agent/consul/autopilotevents/ready_servers_events_test.go @@ -4,6 +4,7 @@ import ( "testing" time "time" + "github.com/hashicorp/go-memdb" "github.com/hashicorp/raft" autopilot "github.com/hashicorp/raft-autopilot" mock "github.com/stretchr/testify/mock" @@ -164,9 +165,21 @@ func TestAutopilotStateToReadyServersWithTaggedAddresses(t *testing.T) { types.NodeID("792ae13c-d765-470b-852c-e073fdb6e849"), structs.NodeEnterpriseMetaInDefaultPartition(), structs.DefaultPeerKeyword, + ).Times(2).Return( + uint64(0), + &structs.Node{Node: "node-1", TaggedAddresses: map[string]string{"wan": "5.4.3.2"}}, + nil, + ) + + store.On("NodeService", + memdb.WatchSet(nil), + "node-1", + structs.ConsulServiceID, + structs.NodeEnterpriseMetaInDefaultPartition(), + structs.DefaultPeerKeyword, ).Once().Return( uint64(0), - &structs.Node{TaggedAddresses: map[string]string{"wan": "5.4.3.2"}}, + nil, nil, ) @@ -174,9 +187,21 @@ func TestAutopilotStateToReadyServersWithTaggedAddresses(t *testing.T) { types.NodeID("65e79ff4-bbce-467b-a9d6-725c709fa985"), structs.NodeEnterpriseMetaInDefaultPartition(), structs.DefaultPeerKeyword, + ).Times(2).Return( + uint64(0), + &structs.Node{Node: "node-2", TaggedAddresses: map[string]string{"wan": "1.2.3.4"}}, + nil, + ) + + store.On("NodeService", + memdb.WatchSet(nil), + "node-2", + structs.ConsulServiceID, + structs.NodeEnterpriseMetaInDefaultPartition(), + structs.DefaultPeerKeyword, ).Once().Return( uint64(0), - &structs.Node{TaggedAddresses: map[string]string{"wan": "1.2.3.4"}}, + nil, nil, ) @@ -184,9 +209,119 @@ func TestAutopilotStateToReadyServersWithTaggedAddresses(t *testing.T) { types.NodeID("db11f0ac-0cbe-4215-80cc-b4e843f4df1e"), structs.NodeEnterpriseMetaInDefaultPartition(), structs.DefaultPeerKeyword, + ).Times(2).Return( + uint64(0), + &structs.Node{Node: "node-3", TaggedAddresses: map[string]string{"wan": "9.8.7.6"}}, + nil, + ) + + store.On("NodeService", + memdb.WatchSet(nil), + "node-3", + structs.ConsulServiceID, + structs.NodeEnterpriseMetaInDefaultPartition(), + structs.DefaultPeerKeyword, ).Once().Return( uint64(0), - &structs.Node{TaggedAddresses: map[string]string{"wan": "9.8.7.6"}}, + nil, + nil, + ) + + r := NewReadyServersEventPublisher(Config{ + GetStore: func() StateStore { return store }, + }) + + actual := r.autopilotStateToReadyServers(exampleState) + require.ElementsMatch(t, expected, actual) +} + +func TestAutopilotStateToReadyServersWithExtGRPCPort(t *testing.T) { + expected := EventPayloadReadyServers{ + { + ID: "792ae13c-d765-470b-852c-e073fdb6e849", + Address: "198.18.0.2", + ExtGRPCPort: 1234, + Version: "v1.12.0", + }, + { + ID: "65e79ff4-bbce-467b-a9d6-725c709fa985", + Address: "198.18.0.3", + ExtGRPCPort: 2345, + Version: "v1.12.0", + }, + { + ID: "db11f0ac-0cbe-4215-80cc-b4e843f4df1e", + Address: "198.18.0.4", + ExtGRPCPort: 3456, + Version: "v1.12.0", + }, + } + + store := &MockStateStore{} + t.Cleanup(func() { store.AssertExpectations(t) }) + store.On("GetNodeID", + types.NodeID("792ae13c-d765-470b-852c-e073fdb6e849"), + structs.NodeEnterpriseMetaInDefaultPartition(), + structs.DefaultPeerKeyword, + ).Times(2).Return( + uint64(0), + &structs.Node{Node: "node-1"}, + nil, + ) + + store.On("NodeService", + memdb.WatchSet(nil), + "node-1", + structs.ConsulServiceID, + structs.NodeEnterpriseMetaInDefaultPartition(), + structs.DefaultPeerKeyword, + ).Once().Return( + uint64(0), + &structs.NodeService{Meta: map[string]string{"grpc_port": "1234"}}, + nil, + ) + + store.On("GetNodeID", + types.NodeID("65e79ff4-bbce-467b-a9d6-725c709fa985"), + structs.NodeEnterpriseMetaInDefaultPartition(), + structs.DefaultPeerKeyword, + ).Times(2).Return( + uint64(0), + &structs.Node{Node: "node-2"}, + nil, + ) + + store.On("NodeService", + memdb.WatchSet(nil), + "node-2", + structs.ConsulServiceID, + structs.NodeEnterpriseMetaInDefaultPartition(), + structs.DefaultPeerKeyword, + ).Once().Return( + uint64(0), + &structs.NodeService{Meta: map[string]string{"grpc_port": "2345"}}, + nil, + ) + + store.On("GetNodeID", + types.NodeID("db11f0ac-0cbe-4215-80cc-b4e843f4df1e"), + structs.NodeEnterpriseMetaInDefaultPartition(), + structs.DefaultPeerKeyword, + ).Times(2).Return( + uint64(0), + &structs.Node{Node: "node-3"}, + nil, + ) + + store.On("NodeService", + memdb.WatchSet(nil), + "node-3", + structs.ConsulServiceID, + structs.NodeEnterpriseMetaInDefaultPartition(), + structs.DefaultPeerKeyword, + ).Once().Return( + uint64(0), + &structs.NodeService{Meta: map[string]string{"grpc_port": "3456"}}, nil, ) @@ -493,9 +628,21 @@ func TestReadyServerEventsSnapshotHandler(t *testing.T) { types.NodeID("792ae13c-d765-470b-852c-e073fdb6e849"), structs.NodeEnterpriseMetaInDefaultPartition(), structs.DefaultPeerKeyword, + ).Times(2).Return( + uint64(0), + &structs.Node{Node: "node-1", TaggedAddresses: map[string]string{"wan": "5.4.3.2"}}, + nil, + ) + + store.On("NodeService", + memdb.WatchSet(nil), + "node-1", + structs.ConsulServiceID, + structs.NodeEnterpriseMetaInDefaultPartition(), + structs.DefaultPeerKeyword, ).Once().Return( uint64(0), - &structs.Node{TaggedAddresses: map[string]string{"wan": "5.4.3.2"}}, + nil, nil, ) @@ -503,9 +650,21 @@ func TestReadyServerEventsSnapshotHandler(t *testing.T) { types.NodeID("65e79ff4-bbce-467b-a9d6-725c709fa985"), structs.NodeEnterpriseMetaInDefaultPartition(), structs.DefaultPeerKeyword, + ).Times(2).Return( + uint64(0), + &structs.Node{Node: "node-2", TaggedAddresses: map[string]string{"wan": "1.2.3.4"}}, + nil, + ) + + store.On("NodeService", + memdb.WatchSet(nil), + "node-2", + structs.ConsulServiceID, + structs.NodeEnterpriseMetaInDefaultPartition(), + structs.DefaultPeerKeyword, ).Once().Return( uint64(0), - &structs.Node{TaggedAddresses: map[string]string{"wan": "1.2.3.4"}}, + nil, nil, ) @@ -513,9 +672,21 @@ func TestReadyServerEventsSnapshotHandler(t *testing.T) { types.NodeID("db11f0ac-0cbe-4215-80cc-b4e843f4df1e"), structs.NodeEnterpriseMetaInDefaultPartition(), structs.DefaultPeerKeyword, + ).Times(2).Return( + uint64(0), + &structs.Node{Node: "node-3", TaggedAddresses: map[string]string{"wan": "9.8.7.6"}}, + nil, + ) + + store.On("NodeService", + memdb.WatchSet(nil), + "node-3", + structs.ConsulServiceID, + structs.NodeEnterpriseMetaInDefaultPartition(), + structs.DefaultPeerKeyword, ).Once().Return( uint64(0), - &structs.Node{TaggedAddresses: map[string]string{"wan": "9.8.7.6"}}, + nil, nil, ) diff --git a/agent/consul/config_endpoint.go b/agent/consul/config_endpoint.go index 0926fe9f5..ddf19916a 100644 --- a/agent/consul/config_endpoint.go +++ b/agent/consul/config_endpoint.go @@ -12,6 +12,7 @@ import ( hashstructure_v2 "github.com/mitchellh/hashstructure/v2" "github.com/hashicorp/consul/acl" + "github.com/hashicorp/consul/agent/configentry" "github.com/hashicorp/consul/agent/consul/state" "github.com/hashicorp/consul/agent/structs" ) @@ -510,7 +511,7 @@ func (c *ConfigEntry) ResolveServiceConfig(args *structs.ServiceConfigRequest, r ranOnce = true } - thisReply, err := computeResolvedServiceConfig( + thisReply, err := configentry.ComputeResolvedServiceConfig( args, upstreamIDs, legacyUpstreams, diff --git a/agent/consul/kvs_endpoint.go b/agent/consul/kvs_endpoint.go index 434ebcada..3f2cbe1cc 100644 --- a/agent/consul/kvs_endpoint.go +++ b/agent/consul/kvs_endpoint.go @@ -49,7 +49,7 @@ func kvsPreApply(logger hclog.Logger, srv *Server, authz resolver.Result, op api return false, err } - case api.KVGet, api.KVGetTree: + case api.KVGet, api.KVGetTree, api.KVGetOrEmpty: // Filtering for GETs is done on the output side. case api.KVCheckSession, api.KVCheckIndex: diff --git a/agent/consul/leader_connect_ca.go b/agent/consul/leader_connect_ca.go index fe780f7f2..4de55ba03 100644 --- a/agent/consul/leader_connect_ca.go +++ b/agent/consul/leader_connect_ca.go @@ -1451,6 +1451,19 @@ func (c *CAManager) AuthorizeAndSignCertificate(csr *x509.CertificateRequest, au return nil, connect.InvalidCSRError("SPIFFE ID in CSR from a different datacenter: %s, "+ "we are %s", v.Datacenter, dc) } + case *connect.SpiffeIDServer: + // The authorizer passed in should have unlimited permissions. + if err := allow.ACLWriteAllowed(&authzContext); err != nil { + return nil, err + } + + // Verify that the DC in the URI matches us. + // The request must have been issued by a local server. + dc := c.serverConf.Datacenter + if v.Datacenter != dc { + return nil, connect.InvalidCSRError("SPIFFE ID in CSR from a different datacenter: %s, "+ + "we are %s", v.Datacenter, dc) + } default: return nil, connect.InvalidCSRError("SPIFFE ID in CSR must be a service or agent ID") } @@ -1472,9 +1485,11 @@ func (c *CAManager) SignCertificate(csr *x509.CertificateRequest, spiffeID conne if err != nil { return nil, err } + signingID := connect.SpiffeIDSigningForCluster(config.ClusterID) serviceID, isService := spiffeID.(*connect.SpiffeIDService) agentID, isAgent := spiffeID.(*connect.SpiffeIDAgent) + serverID, isServer := spiffeID.(*connect.SpiffeIDServer) mgwID, isMeshGateway := spiffeID.(*connect.SpiffeIDMeshGateway) var entMeta acl.EnterpriseMeta @@ -1493,6 +1508,12 @@ func (c *CAManager) SignCertificate(csr *x509.CertificateRequest, spiffeID conne } entMeta.Merge(mgwID.GetEnterpriseMeta()) + case isServer: + if !signingID.CanSign(spiffeID) { + return nil, connect.InvalidCSRError("SPIFFE ID in CSR from a different trust domain: %s, "+ + "we are %s", serverID.Host, signingID.Host()) + } + entMeta.Normalize() case isAgent: // isAgent - if we support more ID types then this would need to be an else if // here we are just automatically fixing the trust domain. For auto-encrypt and @@ -1519,7 +1540,7 @@ func (c *CAManager) SignCertificate(csr *x509.CertificateRequest, spiffeID conne entMeta.Merge(agentID.GetEnterpriseMeta()) default: - return nil, connect.InvalidCSRError("SPIFFE ID in CSR must be a service, agent, or mesh gateway ID") + return nil, connect.InvalidCSRError("SPIFFE ID in CSR must be a service, agent, server, or mesh gateway ID") } commonCfg, err := config.GetCommonConfig() @@ -1608,6 +1629,8 @@ func (c *CAManager) SignCertificate(csr *x509.CertificateRequest, spiffeID conne case isAgent: reply.Agent = agentID.Agent reply.AgentURI = cert.URIs[0].String() + case isServer: + reply.ServerURI = cert.URIs[0].String() default: return nil, errors.New("not possible") } diff --git a/agent/consul/leader_connect_ca_test.go b/agent/consul/leader_connect_ca_test.go index 91095be8e..9de808bd1 100644 --- a/agent/consul/leader_connect_ca_test.go +++ b/agent/consul/leader_connect_ca_test.go @@ -1042,3 +1042,43 @@ func setupPrimaryCA(t *testing.T, client *vaultapi.Client, path string, rootPEM require.NoError(t, err, "failed to set signed intermediate") return lib.EnsureTrailingNewline(buf.String()) } + +func TestCAManager_Sign_SpiffeIDServer(t *testing.T) { + if testing.Short() { + t.Skip("too slow for testing.Short") + } + + _, s1 := testServerWithConfig(t) + testrpc.WaitForTestAgent(t, s1.RPC, "dc1") + + codec := rpcClient(t, s1) + roots := structs.IndexedCARoots{} + + retry.Run(t, func(r *retry.R) { + err := msgpackrpc.CallWithCodec(codec, "ConnectCA.Roots", &structs.DCSpecificRequest{}, &roots) + require.NoError(r, err) + require.Len(r, roots.Roots, 1) + }) + + pk, _, err := connect.GeneratePrivateKey() + require.NoError(t, err) + + // Request a leaf certificate for a server. + spiffeID := &connect.SpiffeIDServer{ + Host: roots.TrustDomain, + Datacenter: "dc1", + } + csr, err := connect.CreateCSR(spiffeID, pk, nil, nil) + require.NoError(t, err) + + req := structs.CASignRequest{CSR: csr} + cert := structs.IssuedCert{} + err = msgpackrpc.CallWithCodec(codec, "ConnectCA.Sign", &req, &cert) + require.NoError(t, err) + + // Verify the chain of trust. + verifyLeafCert(t, roots.Roots[0], cert.CertPEM) + + // Verify the Server's URI. + require.Equal(t, fmt.Sprintf("spiffe://%s/agent/server/dc/dc1", roots.TrustDomain), cert.ServerURI) +} diff --git a/agent/consul/merge_service_config.go b/agent/consul/merge_service_config.go index 91fe229ea..706e24f4a 100644 --- a/agent/consul/merge_service_config.go +++ b/agent/consul/merge_service_config.go @@ -3,13 +3,14 @@ package consul import ( "fmt" - "github.com/hashicorp/consul/agent/configentry" - "github.com/hashicorp/consul/agent/consul/state" - "github.com/hashicorp/consul/agent/structs" "github.com/hashicorp/go-hclog" memdb "github.com/hashicorp/go-memdb" "github.com/imdario/mergo" "github.com/mitchellh/copystructure" + + "github.com/hashicorp/consul/agent/configentry" + "github.com/hashicorp/consul/agent/consul/state" + "github.com/hashicorp/consul/agent/structs" ) // mergeNodeServiceWithCentralConfig merges a service instance (NodeService) with the @@ -66,7 +67,7 @@ func mergeNodeServiceWithCentralConfig( ns.ID, err) } - defaults, err := computeResolvedServiceConfig( + defaults, err := configentry.ComputeResolvedServiceConfig( configReq, upstreams, false, @@ -87,225 +88,6 @@ func mergeNodeServiceWithCentralConfig( return cfgIndex, mergedns, nil } -func computeResolvedServiceConfig( - args *structs.ServiceConfigRequest, - upstreamIDs []structs.ServiceID, - legacyUpstreams bool, - entries *configentry.ResolvedServiceConfigSet, - logger hclog.Logger, -) (*structs.ServiceConfigResponse, error) { - var thisReply structs.ServiceConfigResponse - - thisReply.MeshGateway.Mode = structs.MeshGatewayModeDefault - - // TODO(freddy) Refactor this into smaller set of state store functions - // Pass the WatchSet to both the service and proxy config lookups. If either is updated during the - // blocking query, this function will be rerun and these state store lookups will both be current. - // We use the default enterprise meta to look up the global proxy defaults because they are not namespaced. - var proxyConfGlobalProtocol string - proxyConf := entries.GetProxyDefaults(args.PartitionOrDefault()) - if proxyConf != nil { - // Apply the proxy defaults to the sidecar's proxy config - mapCopy, err := copystructure.Copy(proxyConf.Config) - if err != nil { - return nil, fmt.Errorf("failed to copy global proxy-defaults: %v", err) - } - thisReply.ProxyConfig = mapCopy.(map[string]interface{}) - thisReply.Mode = proxyConf.Mode - thisReply.TransparentProxy = proxyConf.TransparentProxy - thisReply.MeshGateway = proxyConf.MeshGateway - thisReply.Expose = proxyConf.Expose - - // Extract the global protocol from proxyConf for upstream configs. - rawProtocol := proxyConf.Config["protocol"] - if rawProtocol != nil { - var ok bool - proxyConfGlobalProtocol, ok = rawProtocol.(string) - if !ok { - return nil, fmt.Errorf("invalid protocol type %T", rawProtocol) - } - } - } - - serviceConf := entries.GetServiceDefaults( - structs.NewServiceID(args.Name, &args.EnterpriseMeta), - ) - if serviceConf != nil { - if serviceConf.Expose.Checks { - thisReply.Expose.Checks = true - } - if len(serviceConf.Expose.Paths) >= 1 { - thisReply.Expose.Paths = serviceConf.Expose.Paths - } - if serviceConf.MeshGateway.Mode != structs.MeshGatewayModeDefault { - thisReply.MeshGateway.Mode = serviceConf.MeshGateway.Mode - } - if serviceConf.Protocol != "" { - if thisReply.ProxyConfig == nil { - thisReply.ProxyConfig = make(map[string]interface{}) - } - thisReply.ProxyConfig["protocol"] = serviceConf.Protocol - } - if serviceConf.TransparentProxy.OutboundListenerPort != 0 { - thisReply.TransparentProxy.OutboundListenerPort = serviceConf.TransparentProxy.OutboundListenerPort - } - if serviceConf.TransparentProxy.DialedDirectly { - thisReply.TransparentProxy.DialedDirectly = serviceConf.TransparentProxy.DialedDirectly - } - if serviceConf.Mode != structs.ProxyModeDefault { - thisReply.Mode = serviceConf.Mode - } - if serviceConf.Destination != nil { - thisReply.Destination = *serviceConf.Destination - } - - if serviceConf.MaxInboundConnections > 0 { - if thisReply.ProxyConfig == nil { - thisReply.ProxyConfig = map[string]interface{}{} - } - thisReply.ProxyConfig["max_inbound_connections"] = serviceConf.MaxInboundConnections - } - - thisReply.Meta = serviceConf.Meta - } - - // First collect all upstreams into a set of seen upstreams. - // Upstreams can come from: - // - Explicitly from proxy registrations, and therefore as an argument to this RPC endpoint - // - Implicitly from centralized upstream config in service-defaults - seenUpstreams := map[structs.ServiceID]struct{}{} - - var ( - noUpstreamArgs = len(upstreamIDs) == 0 && len(args.Upstreams) == 0 - - // Check the args and the resolved value. If it was exclusively set via a config entry, then args.Mode - // will never be transparent because the service config request does not use the resolved value. - tproxy = args.Mode == structs.ProxyModeTransparent || thisReply.Mode == structs.ProxyModeTransparent - ) - - // The upstreams passed as arguments to this endpoint are the upstreams explicitly defined in a proxy registration. - // If no upstreams were passed, then we should only return the resolved config if the proxy is in transparent mode. - // Otherwise we would return a resolved upstream config to a proxy with no configured upstreams. - if noUpstreamArgs && !tproxy { - return &thisReply, nil - } - - // First store all upstreams that were provided in the request - for _, sid := range upstreamIDs { - if _, ok := seenUpstreams[sid]; !ok { - seenUpstreams[sid] = struct{}{} - } - } - - // Then store upstreams inferred from service-defaults and mapify the overrides. - var ( - upstreamConfigs = make(map[structs.ServiceID]*structs.UpstreamConfig) - upstreamDefaults *structs.UpstreamConfig - // usConfigs stores the opaque config map for each upstream and is keyed on the upstream's ID. - usConfigs = make(map[structs.ServiceID]map[string]interface{}) - ) - if serviceConf != nil && serviceConf.UpstreamConfig != nil { - for i, override := range serviceConf.UpstreamConfig.Overrides { - if override.Name == "" { - logger.Warn( - "Skipping UpstreamConfig.Overrides entry without a required name field", - "entryIndex", i, - "kind", serviceConf.GetKind(), - "name", serviceConf.GetName(), - "namespace", serviceConf.GetEnterpriseMeta().NamespaceOrEmpty(), - ) - continue // skip this impossible condition - } - seenUpstreams[override.ServiceID()] = struct{}{} - upstreamConfigs[override.ServiceID()] = override - } - if serviceConf.UpstreamConfig.Defaults != nil { - upstreamDefaults = serviceConf.UpstreamConfig.Defaults - - // Store the upstream defaults under a wildcard key so that they can be applied to - // upstreams that are inferred from intentions and do not have explicit upstream configuration. - cfgMap := make(map[string]interface{}) - upstreamDefaults.MergeInto(cfgMap) - - wildcard := structs.NewServiceID(structs.WildcardSpecifier, args.WithWildcardNamespace()) - usConfigs[wildcard] = cfgMap - } - } - - for upstream := range seenUpstreams { - resolvedCfg := make(map[string]interface{}) - - // The protocol of an upstream is resolved in this order: - // 1. Default protocol from proxy-defaults (how all services should be addressed) - // 2. Protocol for upstream service defined in its service-defaults (how the upstream wants to be addressed) - // 3. Protocol defined for the upstream in the service-defaults.(upstream_config.defaults|upstream_config.overrides) of the downstream - // (how the downstream wants to address it) - protocol := proxyConfGlobalProtocol - - upstreamSvcDefaults := entries.GetServiceDefaults( - structs.NewServiceID(upstream.ID, &upstream.EnterpriseMeta), - ) - if upstreamSvcDefaults != nil { - if upstreamSvcDefaults.Protocol != "" { - protocol = upstreamSvcDefaults.Protocol - } - } - - if protocol != "" { - resolvedCfg["protocol"] = protocol - } - - // Merge centralized defaults for all upstreams before configuration for specific upstreams - if upstreamDefaults != nil { - upstreamDefaults.MergeInto(resolvedCfg) - } - - // The MeshGateway value from the proxy registration overrides the one from upstream_defaults - // because it is specific to the proxy instance. - // - // The goal is to flatten the mesh gateway mode in this order: - // 0. Value from centralized upstream_defaults - // 1. Value from local proxy registration - // 2. Value from centralized upstream_config - // 3. Value from local upstream definition. This last step is done in the client's service manager. - if !args.MeshGateway.IsZero() { - resolvedCfg["mesh_gateway"] = args.MeshGateway - } - - if upstreamConfigs[upstream] != nil { - upstreamConfigs[upstream].MergeInto(resolvedCfg) - } - - if len(resolvedCfg) > 0 { - usConfigs[upstream] = resolvedCfg - } - } - - // don't allocate the slices just to not fill them - if len(usConfigs) == 0 { - return &thisReply, nil - } - - if legacyUpstreams { - // For legacy upstreams we return a map that is only keyed on the string ID, since they precede namespaces - thisReply.UpstreamConfigs = make(map[string]map[string]interface{}) - - for us, conf := range usConfigs { - thisReply.UpstreamConfigs[us.ID] = conf - } - - } else { - thisReply.UpstreamIDConfigs = make(structs.OpaqueUpstreamConfigs, 0, len(usConfigs)) - - for us, conf := range usConfigs { - thisReply.UpstreamIDConfigs = append(thisReply.UpstreamIDConfigs, - structs.OpaqueUpstreamConfig{Upstream: us, Config: conf}) - } - } - - return &thisReply, nil -} - // MergeServiceConfig merges the service into defaults to produce the final effective // config for the specified service. func MergeServiceConfig(defaults *structs.ServiceConfigResponse, service *structs.NodeService) (*structs.NodeService, error) { diff --git a/agent/consul/merge_service_config_test.go b/agent/consul/merge_service_config_test.go index a497579a7..a4b88308e 100644 --- a/agent/consul/merge_service_config_test.go +++ b/agent/consul/merge_service_config_test.go @@ -3,60 +3,13 @@ package consul import ( "testing" - "github.com/hashicorp/consul/agent/configentry" - "github.com/hashicorp/consul/agent/structs" "github.com/mitchellh/copystructure" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + + "github.com/hashicorp/consul/agent/structs" ) -func Test_ComputeResolvedServiceConfig(t *testing.T) { - type args struct { - scReq *structs.ServiceConfigRequest - upstreamIDs []structs.ServiceID - entries *configentry.ResolvedServiceConfigSet - } - - sid := structs.ServiceID{ - ID: "sid", - EnterpriseMeta: *structs.DefaultEnterpriseMetaInDefaultPartition(), - } - tests := []struct { - name string - args args - want *structs.ServiceConfigResponse - }{ - { - name: "proxy with maxinboundsconnections", - args: args{ - scReq: &structs.ServiceConfigRequest{ - Name: "sid", - }, - entries: &configentry.ResolvedServiceConfigSet{ - ServiceDefaults: map[structs.ServiceID]*structs.ServiceConfigEntry{ - sid: { - MaxInboundConnections: 20, - }, - }, - }, - }, - want: &structs.ServiceConfigResponse{ - ProxyConfig: map[string]interface{}{ - "max_inbound_connections": 20, - }, - }, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got, err := computeResolvedServiceConfig(tt.args.scReq, tt.args.upstreamIDs, - false, tt.args.entries, nil) - require.NoError(t, err) - assert.Equal(t, tt.want, got) - }) - } -} - func Test_MergeServiceConfig_TransparentProxy(t *testing.T) { type args struct { defaults *structs.ServiceConfigResponse diff --git a/agent/consul/state/txn.go b/agent/consul/state/txn.go index 087bb4fe8..af6e98995 100644 --- a/agent/consul/state/txn.go +++ b/agent/consul/state/txn.go @@ -60,6 +60,13 @@ func (s *Store) txnKVS(tx WriteTxn, idx uint64, op *structs.TxnKVOp) (structs.Tx err = fmt.Errorf("key %q doesn't exist", op.DirEnt.Key) } + case api.KVGetOrEmpty: + _, entry, err = kvsGetTxn(tx, nil, op.DirEnt.Key, op.DirEnt.EnterpriseMeta) + if entry == nil && err == nil { + entry = &op.DirEnt + entry.Value = nil + } + case api.KVGetTree: var entries structs.DirEntries _, entries, err = s.kvsListTxn(tx, nil, op.DirEnt.Key, op.DirEnt.EnterpriseMeta) @@ -95,7 +102,7 @@ func (s *Store) txnKVS(tx WriteTxn, idx uint64, op *structs.TxnKVOp) (structs.Tx // value (we have to clone so we don't modify the entry being used by // the state store). if entry != nil { - if op.Verb == api.KVGet { + if op.Verb == api.KVGet || op.Verb == api.KVGetOrEmpty { result := structs.TxnResult{KV: entry} return structs.TxnResults{&result}, nil } diff --git a/agent/consul/state/txn_test.go b/agent/consul/state/txn_test.go index f98325df3..a7694089b 100644 --- a/agent/consul/state/txn_test.go +++ b/agent/consul/state/txn_test.go @@ -577,6 +577,22 @@ func TestStateStore_Txn_KVS(t *testing.T) { }, }, }, + &structs.TxnOp{ + KV: &structs.TxnKVOp{ + Verb: api.KVGetOrEmpty, + DirEnt: structs.DirEntry{ + Key: "foo/update", + }, + }, + }, + &structs.TxnOp{ + KV: &structs.TxnKVOp{ + Verb: api.KVGetOrEmpty, + DirEnt: structs.DirEntry{ + Key: "foo/not-exists", + }, + }, + }, &structs.TxnOp{ KV: &structs.TxnKVOp{ Verb: api.KVCheckIndex, @@ -702,6 +718,22 @@ func TestStateStore_Txn_KVS(t *testing.T) { }, }, }, + &structs.TxnResult{ + KV: &structs.DirEntry{ + Key: "foo/update", + Value: []byte("stale"), + RaftIndex: structs.RaftIndex{ + CreateIndex: 5, + ModifyIndex: 5, + }, + }, + }, + &structs.TxnResult{ + KV: &structs.DirEntry{ + Key: "foo/not-exists", + Value: nil, + }, + }, &structs.TxnResult{ KV: &structs.DirEntry{ diff --git a/agent/grpc-external/services/peerstream/replication.go b/agent/grpc-external/services/peerstream/replication.go index be79a23bd..20bc3b12a 100644 --- a/agent/grpc-external/services/peerstream/replication.go +++ b/agent/grpc-external/services/peerstream/replication.go @@ -5,12 +5,13 @@ import ( "fmt" "strings" - "github.com/hashicorp/go-hclog" + "github.com/golang/protobuf/proto" "google.golang.org/genproto/googleapis/rpc/code" newproto "google.golang.org/protobuf/proto" "google.golang.org/protobuf/types/known/anypb" "github.com/hashicorp/consul/agent/cache" + "github.com/hashicorp/consul/agent/consul/state" "github.com/hashicorp/consul/agent/structs" "github.com/hashicorp/consul/proto/pbpeering" "github.com/hashicorp/consul/proto/pbpeerstream" @@ -35,7 +36,6 @@ import ( // Each cache.UpdateEvent will contain all instances for a service name. // If there are no instances in the event, we consider that to be a de-registration. func makeServiceResponse( - logger hclog.Logger, mst *MutableStatus, update cache.UpdateEvent, ) (*pbpeerstream.ReplicationMessage_Response, error) { @@ -87,7 +87,6 @@ func makeServiceResponse( } func makeCARootsResponse( - logger hclog.Logger, update cache.UpdateEvent, ) (*pbpeerstream.ReplicationMessage_Response, error) { any, _, err := marshalToProtoAny[*pbpeering.PeeringTrustBundle](update.Result) @@ -105,6 +104,24 @@ func makeCARootsResponse( }, nil } +func makeServerAddrsResponse( + update cache.UpdateEvent, +) (*pbpeerstream.ReplicationMessage_Response, error) { + any, _, err := marshalToProtoAny[*pbpeering.PeeringServerAddresses](update.Result) + if err != nil { + return nil, fmt.Errorf("failed to marshal: %w", err) + } + + return &pbpeerstream.ReplicationMessage_Response{ + ResourceURL: pbpeerstream.TypeURLPeeringServerAddresses, + // TODO(peering): Nonce management + Nonce: "", + ResourceID: "server-addrs", + Operation: pbpeerstream.Operation_OPERATION_UPSERT, + Resource: any, + }, nil +} + // marshalToProtoAny takes any input and returns: // the protobuf.Any type, the asserted T type, and any errors // during marshalling or type assertion. @@ -127,7 +144,6 @@ func (s *Server) processResponse( partition string, mutableStatus *MutableStatus, resp *pbpeerstream.ReplicationMessage_Response, - logger hclog.Logger, ) (*pbpeerstream.ReplicationMessage, error) { if !pbpeerstream.KnownTypeURL(resp.ResourceURL) { err := fmt.Errorf("received response for unknown resource type %q", resp.ResourceURL) @@ -151,7 +167,7 @@ func (s *Server) processResponse( ), err } - if err := s.handleUpsert(peerName, partition, mutableStatus, resp.ResourceURL, resp.ResourceID, resp.Resource, logger); err != nil { + if err := s.handleUpsert(peerName, partition, mutableStatus, resp.ResourceURL, resp.ResourceID, resp.Resource); err != nil { return makeNACKReply( resp.ResourceURL, resp.Nonce, @@ -163,7 +179,7 @@ func (s *Server) processResponse( return makeACKReply(resp.ResourceURL, resp.Nonce), nil case pbpeerstream.Operation_OPERATION_DELETE: - if err := s.handleDelete(peerName, partition, mutableStatus, resp.ResourceURL, resp.ResourceID, logger); err != nil { + if err := s.handleDelete(peerName, partition, mutableStatus, resp.ResourceURL, resp.ResourceID); err != nil { return makeNACKReply( resp.ResourceURL, resp.Nonce, @@ -196,7 +212,6 @@ func (s *Server) handleUpsert( resourceURL string, resourceID string, resource *anypb.Any, - logger hclog.Logger, ) error { if resource.TypeUrl != resourceURL { return fmt.Errorf("mismatched resourceURL %q and Any typeUrl %q", resourceURL, resource.TypeUrl) @@ -229,15 +244,23 @@ func (s *Server) handleUpsert( return s.handleUpsertRoots(peerName, partition, roots) + case pbpeerstream.TypeURLPeeringServerAddresses: + addrs := &pbpeering.PeeringServerAddresses{} + if err := resource.UnmarshalTo(addrs); err != nil { + return fmt.Errorf("failed to unmarshal resource: %w", err) + } + + return s.handleUpsertServerAddrs(peerName, partition, addrs) default: return fmt.Errorf("unexpected resourceURL: %s", resourceURL) } } // handleUpdateService handles both deletion and upsert events for a service. -// On an UPSERT event: -// - All nodes, services, checks in the input pbNodes are re-applied through Raft. -// - Any nodes, services, or checks in the catalog that were not in the input pbNodes get deleted. +// +// On an UPSERT event: +// - All nodes, services, checks in the input pbNodes are re-applied through Raft. +// - Any nodes, services, or checks in the catalog that were not in the input pbNodes get deleted. // // On a DELETE event: // - A reconciliation against nil or empty input pbNodes leads to deleting all stored catalog resources @@ -449,13 +472,39 @@ func (s *Server) handleUpsertRoots( return s.Backend.PeeringTrustBundleWrite(req) } +func (s *Server) handleUpsertServerAddrs( + peerName string, + partition string, + addrs *pbpeering.PeeringServerAddresses, +) error { + q := state.Query{ + Value: peerName, + EnterpriseMeta: *structs.DefaultEnterpriseMetaInPartition(partition), + } + _, existing, err := s.GetStore().PeeringRead(nil, q) + if err != nil { + return fmt.Errorf("failed to read peering: %w", err) + } + if existing == nil || !existing.IsActive() { + return fmt.Errorf("peering does not exist or has been marked for deletion") + } + + // Clone to avoid mutating the existing data + p := proto.Clone(existing).(*pbpeering.Peering) + p.PeerServerAddresses = addrs.GetAddresses() + + req := &pbpeering.PeeringWriteRequest{ + Peering: p, + } + return s.Backend.PeeringWrite(req) +} + func (s *Server) handleDelete( peerName string, partition string, mutableStatus *MutableStatus, resourceURL string, resourceID string, - logger hclog.Logger, ) error { switch resourceURL { case pbpeerstream.TypeURLExportedService: diff --git a/agent/grpc-external/services/peerstream/server.go b/agent/grpc-external/services/peerstream/server.go index 17388f4a2..0f0627cb5 100644 --- a/agent/grpc-external/services/peerstream/server.go +++ b/agent/grpc-external/services/peerstream/server.go @@ -105,6 +105,7 @@ type Backend interface { PeeringTrustBundleWrite(req *pbpeering.PeeringTrustBundleWriteRequest) error CatalogRegister(req *structs.RegisterRequest) error CatalogDeregister(req *structs.DeregisterRequest) error + PeeringWrite(req *pbpeering.PeeringWriteRequest) error } // StateStore provides a read-only interface for querying Peering data. diff --git a/agent/grpc-external/services/peerstream/stream_resources.go b/agent/grpc-external/services/peerstream/stream_resources.go index ad5d9d463..bdad21467 100644 --- a/agent/grpc-external/services/peerstream/stream_resources.go +++ b/agent/grpc-external/services/peerstream/stream_resources.go @@ -161,8 +161,22 @@ func (s *Server) StreamResources(stream pbpeerstream.PeerStreamService_StreamRes if p == nil { return grpcstatus.Error(codes.InvalidArgument, "initial subscription for unknown PeerID: "+req.PeerID) } + if !p.IsActive() { + // If peering is terminated, then our peer sent the termination message. + // For other non-active states, send the termination message. + if p.State != pbpeering.PeeringState_TERMINATED { + term := &pbpeerstream.ReplicationMessage{ + Payload: &pbpeerstream.ReplicationMessage_Terminated_{ + Terminated: &pbpeerstream.ReplicationMessage_Terminated{}, + }, + } + logTraceSend(logger, term) - // TODO(peering): If the peering is marked as deleted, send a Terminated message and return + // we don't care if send fails; stream will be killed by termination message or grpc error + _ = stream.Send(term) + } + return grpcstatus.Error(codes.Aborted, "peering is marked as deleted: "+req.PeerID) + } secrets, err := s.GetStore().PeeringSecretsRead(nil, req.PeerID) if err != nil { @@ -347,6 +361,7 @@ func (s *Server) realHandleStream(streamReq HandleStreamRequest) error { for _, resourceURL := range []string{ pbpeerstream.TypeURLExportedService, pbpeerstream.TypeURLPeeringTrustBundle, + pbpeerstream.TypeURLPeeringServerAddresses, } { sub := makeReplicationRequest(&pbpeerstream.ReplicationMessage_Request{ ResourceURL: resourceURL, @@ -544,14 +559,11 @@ func (s *Server) realHandleStream(streamReq HandleStreamRequest) error { // At this point we have a valid ResourceURL and we are subscribed to it. switch { - case req.ResponseNonce == "" && req.Error != nil: - return grpcstatus.Error(codes.InvalidArgument, "initial subscription request for a resource type must not contain an error") - - case req.ResponseNonce != "" && req.Error == nil: // ACK + case req.Error == nil: // ACK // TODO(peering): handle ACK fully status.TrackAck() - case req.ResponseNonce != "" && req.Error != nil: // NACK + case req.Error != nil: // NACK // TODO(peering): handle NACK fully logger.Warn("client peer was unable to apply resource", "code", req.Error.Code, "error", req.Error.Message) status.TrackNack(fmt.Sprintf("client peer was unable to apply resource: %s", req.Error.Message)) @@ -567,7 +579,7 @@ func (s *Server) realHandleStream(streamReq HandleStreamRequest) error { if resp := msg.GetResponse(); resp != nil { // TODO(peering): Ensure there's a nonce - reply, err := s.processResponse(streamReq.PeerName, streamReq.Partition, status, resp, logger) + reply, err := s.processResponse(streamReq.PeerName, streamReq.Partition, status, resp) if err != nil { logger.Error("failed to persist resource", "resourceURL", resp.ResourceURL, "resourceID", resp.ResourceID) status.TrackRecvError(err.Error()) @@ -613,7 +625,7 @@ func (s *Server) realHandleStream(streamReq HandleStreamRequest) error { var resp *pbpeerstream.ReplicationMessage_Response switch { case strings.HasPrefix(update.CorrelationID, subExportedService): - resp, err = makeServiceResponse(logger, status, update) + resp, err = makeServiceResponse(status, update) if err != nil { // Log the error and skip this response to avoid locking up peering due to a bad update event. logger.Error("failed to create service response", "error", err) @@ -624,13 +636,20 @@ func (s *Server) realHandleStream(streamReq HandleStreamRequest) error { // TODO(Peering): figure out how to sync this separately case update.CorrelationID == subCARoot: - resp, err = makeCARootsResponse(logger, update) + resp, err = makeCARootsResponse(update) if err != nil { // Log the error and skip this response to avoid locking up peering due to a bad update event. logger.Error("failed to create ca roots response", "error", err) continue } + case update.CorrelationID == subServerAddrs: + resp, err = makeServerAddrsResponse(update) + if err != nil { + logger.Error("failed to create server address response", "error", err) + continue + } + default: logger.Warn("unrecognized update type from subscription manager: " + update.CorrelationID) continue @@ -641,6 +660,7 @@ func (s *Server) realHandleStream(streamReq HandleStreamRequest) error { replResp := makeReplicationResponse(resp) if err := streamSend(replResp); err != nil { + // note: govet warns of context leak but it is cleaned up in a defer return fmt.Errorf("failed to push data for %q: %w", update.CorrelationID, err) } } diff --git a/agent/grpc-external/services/peerstream/stream_test.go b/agent/grpc-external/services/peerstream/stream_test.go index 9116d7a31..977f7d565 100644 --- a/agent/grpc-external/services/peerstream/stream_test.go +++ b/agent/grpc-external/services/peerstream/stream_test.go @@ -126,7 +126,7 @@ func TestStreamResources_Server_LeaderBecomesFollower(t *testing.T) { // Receive a subscription from a peer. This message arrives while the // server is a leader and should work. - testutil.RunStep(t, "send subscription request to leader and consume its two requests", func(t *testing.T) { + testutil.RunStep(t, "send subscription request to leader and consume its three requests", func(t *testing.T) { sub := &pbpeerstream.ReplicationMessage{ Payload: &pbpeerstream.ReplicationMessage_Open_{ Open: &pbpeerstream.ReplicationMessage_Open{ @@ -145,6 +145,10 @@ func TestStreamResources_Server_LeaderBecomesFollower(t *testing.T) { msg2, err := client.Recv() require.NoError(t, err) require.NotEmpty(t, msg2) + + msg3, err := client.Recv() + require.NoError(t, err) + require.NotEmpty(t, msg3) }) // The ACK will be a new request but at this point the server is not the @@ -1126,7 +1130,7 @@ func TestStreamResources_Server_DisconnectsOnHeartbeatTimeout(t *testing.T) { } srv, store := newTestServer(t, func(c *Config) { - c.incomingHeartbeatTimeout = 5 * time.Millisecond + c.incomingHeartbeatTimeout = 50 * time.Millisecond }) srv.Tracker.setClock(it.Now) @@ -1312,7 +1316,7 @@ func TestStreamResources_Server_KeepsConnectionOpenWithHeartbeat(t *testing.T) { // makeClient sets up a *MockClient with the initial subscription // message handshake. -func makeClient(t *testing.T, srv pbpeerstream.PeerStreamServiceServer, peerID string) *MockClient { +func makeClient(t *testing.T, srv *testServer, peerID string) *MockClient { t.Helper() client := NewMockClient(context.Background()) @@ -1324,7 +1328,7 @@ func makeClient(t *testing.T, srv pbpeerstream.PeerStreamServiceServer, peerID s // Pass errors from server handler into ErrCh so that they can be seen by the client on Recv(). // This matches gRPC's behavior when an error is returned by a server. if err := srv.StreamResources(client.ReplicationStream); err != nil { - errCh <- srv.StreamResources(client.ReplicationStream) + errCh <- err } }() @@ -1343,11 +1347,19 @@ func makeClient(t *testing.T, srv pbpeerstream.PeerStreamServiceServer, peerID s require.NoError(t, err) receivedSub2, err := client.Recv() require.NoError(t, err) + receivedSub3, err := client.Recv() + require.NoError(t, err) - // Issue a services and roots subscription pair to server + // This is required when the client subscribes to server address replication messages. + // We assert for the handler to be called at least once but the data doesn't matter. + srv.mockSnapshotHandler.expect("", 0, 0, nil) + + // Issue services, roots, and server address subscription to server. + // Note that server address may not come as an initial message for _, resourceURL := range []string{ pbpeerstream.TypeURLExportedService, pbpeerstream.TypeURLPeeringTrustBundle, + pbpeerstream.TypeURLPeeringServerAddresses, } { init := &pbpeerstream.ReplicationMessage{ Payload: &pbpeerstream.ReplicationMessage_Request_{ @@ -1383,10 +1395,22 @@ func makeClient(t *testing.T, srv pbpeerstream.PeerStreamServiceServer, peerID s }, }, }, + { + Payload: &pbpeerstream.ReplicationMessage_Request_{ + Request: &pbpeerstream.ReplicationMessage_Request{ + ResourceURL: pbpeerstream.TypeURLPeeringServerAddresses, + // The PeerID field is only set for the messages coming FROM + // the establishing side and are going to be empty from the + // other side. + PeerID: "", + }, + }, + }, } got := []*pbpeerstream.ReplicationMessage{ receivedSub1, receivedSub2, + receivedSub3, } prototest.AssertElementsMatch(t, expect, got) @@ -1443,6 +1467,10 @@ func (b *testStreamBackend) PeeringSecretsWrite(req *pbpeering.SecretsWriteReque return b.store.PeeringSecretsWrite(1, req) } +func (b *testStreamBackend) PeeringWrite(req *pbpeering.PeeringWriteRequest) error { + return b.store.PeeringWrite(1, req) +} + // CatalogRegister mocks catalog registrations through Raft by copying the logic of FSM.applyRegister. func (b *testStreamBackend) CatalogRegister(req *structs.RegisterRequest) error { return b.store.EnsureRegistration(1, req) @@ -1496,7 +1524,7 @@ func Test_makeServiceResponse_ExportedServicesCount(t *testing.T) { }, }, }} - _, err := makeServiceResponse(srv.Logger, mst, update) + _, err := makeServiceResponse(mst, update) require.NoError(t, err) require.Equal(t, 1, mst.GetExportedServicesCount()) @@ -1508,7 +1536,7 @@ func Test_makeServiceResponse_ExportedServicesCount(t *testing.T) { Result: &pbservice.IndexedCheckServiceNodes{ Nodes: []*pbservice.CheckServiceNode{}, }} - _, err := makeServiceResponse(srv.Logger, mst, update) + _, err := makeServiceResponse(mst, update) require.NoError(t, err) require.Equal(t, 0, mst.GetExportedServicesCount()) @@ -1539,7 +1567,7 @@ func Test_processResponse_Validation(t *testing.T) { require.NoError(t, err) run := func(t *testing.T, tc testCase) { - reply, err := srv.processResponse(peerName, "", mst, tc.in, srv.Logger) + reply, err := srv.processResponse(peerName, "", mst, tc.in) if tc.wantErr { require.Error(t, err) } else { @@ -1865,7 +1893,7 @@ func Test_processResponse_handleUpsert_handleDelete(t *testing.T) { } // Simulate an update arriving for billing/api. - _, err = srv.processResponse(peerName, acl.DefaultPartitionName, mst, in, srv.Logger) + _, err = srv.processResponse(peerName, acl.DefaultPartitionName, mst, in) require.NoError(t, err) for svc, expect := range tc.expect { @@ -2731,11 +2759,16 @@ func requireEqualInstances(t *testing.T, expect, got structs.CheckServiceNodes) type testServer struct { *Server + + // mockSnapshotHandler is solely used for handling autopilot events + // which don't come from the state store. + mockSnapshotHandler *mockSnapshotHandler } func newTestServer(t *testing.T, configFn func(c *Config)) (*testServer, *state.Store) { + t.Helper() publisher := stream.NewEventPublisher(10 * time.Second) - store := newStateStore(t, publisher) + store, handler := newStateStore(t, publisher) ports := freeport.GetN(t, 1) // {grpc} @@ -2771,7 +2804,8 @@ func newTestServer(t *testing.T, configFn func(c *Config)) (*testServer, *state. t.Cleanup(grpcServer.Stop) return &testServer{ - Server: srv, + Server: srv, + mockSnapshotHandler: handler, }, store } diff --git a/agent/grpc-external/services/peerstream/subscription_manager.go b/agent/grpc-external/services/peerstream/subscription_manager.go index 0c69b0338..138449e71 100644 --- a/agent/grpc-external/services/peerstream/subscription_manager.go +++ b/agent/grpc-external/services/peerstream/subscription_manager.go @@ -4,6 +4,7 @@ import ( "context" "errors" "fmt" + "strconv" "strings" "github.com/golang/protobuf/proto" @@ -12,6 +13,7 @@ import ( "github.com/hashicorp/consul/acl" "github.com/hashicorp/consul/agent/cache" "github.com/hashicorp/consul/agent/connect" + "github.com/hashicorp/consul/agent/consul/autopilotevents" "github.com/hashicorp/consul/agent/consul/state" "github.com/hashicorp/consul/agent/consul/stream" "github.com/hashicorp/consul/agent/structs" @@ -42,6 +44,7 @@ type subscriptionManager struct { getStore func() StateStore serviceSubReady <-chan struct{} trustBundlesSubReady <-chan struct{} + serverAddrsSubReady <-chan struct{} } // TODO(peering): Maybe centralize so that there is a single manager per datacenter, rather than per peering. @@ -67,6 +70,7 @@ func newSubscriptionManager( getStore: getStore, serviceSubReady: remoteSubTracker.SubscribedChan(pbpeerstream.TypeURLExportedService), trustBundlesSubReady: remoteSubTracker.SubscribedChan(pbpeerstream.TypeURLPeeringTrustBundle), + serverAddrsSubReady: remoteSubTracker.SubscribedChan(pbpeerstream.TypeURLPeeringServerAddresses), } } @@ -83,6 +87,7 @@ func (m *subscriptionManager) subscribe(ctx context.Context, peerID, peerName, p // Wrap our bare state store queries in goroutines that emit events. go m.notifyExportedServicesForPeerID(ctx, state, peerID) + go m.notifyServerAddrUpdates(ctx, state.updateCh) if m.config.ConnectEnabled { go m.notifyMeshGatewaysForPartition(ctx, state, state.partition) // If connect is enabled, watch for updates to CA roots. @@ -262,6 +267,17 @@ func (m *subscriptionManager) handleEvent(ctx context.Context, state *subscripti state.sendPendingEvents(ctx, m.logger, pending) + case u.CorrelationID == subServerAddrs: + addrs, ok := u.Result.(*pbpeering.PeeringServerAddresses) + if !ok { + return fmt.Errorf("invalid type for response: %T", u.Result) + } + pending := &pendingPayload{} + if err := pending.Add(serverAddrsPayloadID, u.CorrelationID, addrs); err != nil { + return err + } + + state.sendPendingEvents(ctx, m.logger, pending) default: return fmt.Errorf("unknown correlation ID: %s", u.CorrelationID) } @@ -333,6 +349,8 @@ func (m *subscriptionManager) notifyRootCAUpdatesForPartition( } } +const subCARoot = "roots" + // subscribeCARoots subscribes to state.EventTopicCARoots for changes to CA roots. // Upon receiving an event it will send the payload in updateCh. func (m *subscriptionManager) subscribeCARoots( @@ -414,8 +432,6 @@ func (m *subscriptionManager) subscribeCARoots( } } -const subCARoot = "roots" - func (m *subscriptionManager) syncNormalServices( ctx context.Context, state *subscriptionState, @@ -721,3 +737,112 @@ const syntheticProxyNameSuffix = "-sidecar-proxy" func generateProxyNameForDiscoveryChain(sn structs.ServiceName) structs.ServiceName { return structs.NewServiceName(sn.Name+syntheticProxyNameSuffix, &sn.EnterpriseMeta) } + +const subServerAddrs = "server-addrs" + +func (m *subscriptionManager) notifyServerAddrUpdates( + ctx context.Context, + updateCh chan<- cache.UpdateEvent, +) { + // Wait until this is subscribed-to. + select { + case <-m.serverAddrsSubReady: + case <-ctx.Done(): + return + } + + var idx uint64 + // TODO(peering): retry logic; fail past a threshold + for { + var err error + // Typically, this function will block inside `m.subscribeServerAddrs` and only return on error. + // Errors are logged and the watch is retried. + idx, err = m.subscribeServerAddrs(ctx, idx, updateCh) + if errors.Is(err, stream.ErrSubForceClosed) { + m.logger.Trace("subscription force-closed due to an ACL change or snapshot restore, will attempt resume") + } else if !errors.Is(err, context.Canceled) && !errors.Is(err, context.DeadlineExceeded) { + m.logger.Warn("failed to subscribe to server addresses, will attempt resume", "error", err.Error()) + } else { + m.logger.Trace(err.Error()) + } + + select { + case <-ctx.Done(): + return + default: + } + } +} + +func (m *subscriptionManager) subscribeServerAddrs( + ctx context.Context, + idx uint64, + updateCh chan<- cache.UpdateEvent, +) (uint64, error) { + // following code adapted from serverdiscovery/watch_servers.go + sub, err := m.backend.Subscribe(&stream.SubscribeRequest{ + Topic: autopilotevents.EventTopicReadyServers, + Subject: stream.SubjectNone, + Token: "", // using anonymous token for now + Index: idx, + }) + if err != nil { + return 0, fmt.Errorf("failed to subscribe to ReadyServers events: %w", err) + } + defer sub.Unsubscribe() + + for { + event, err := sub.Next(ctx) + switch { + case errors.Is(err, context.Canceled): + return 0, err + case err != nil: + return idx, err + } + + // We do not send framing events (e.g. EndOfSnapshot, NewSnapshotToFollow) + // because we send a full list of ready servers on every event, rather than expecting + // clients to maintain a state-machine in the way they do for service health. + if event.IsFramingEvent() { + continue + } + + // Note: this check isn't strictly necessary because the event publishing + // machinery will ensure the index increases monotonically, but it can be + // tricky to faithfully reproduce this in tests (e.g. the EventPublisher + // garbage collects topic buffers and snapshots aggressively when streams + // disconnect) so this avoids a bunch of confusing setup code. + if event.Index <= idx { + continue + } + + idx = event.Index + + payload, ok := event.Payload.(autopilotevents.EventPayloadReadyServers) + if !ok { + return 0, fmt.Errorf("unexpected event payload type: %T", payload) + } + + var serverAddrs = make([]string, 0, len(payload)) + + for _, srv := range payload { + if srv.ExtGRPCPort == 0 { + continue + } + grpcAddr := srv.Address + ":" + strconv.Itoa(srv.ExtGRPCPort) + serverAddrs = append(serverAddrs, grpcAddr) + } + + if len(serverAddrs) == 0 { + m.logger.Warn("did not find any server addresses with external gRPC ports to publish") + continue + } + + updateCh <- cache.UpdateEvent{ + CorrelationID: subServerAddrs, + Result: &pbpeering.PeeringServerAddresses{ + Addresses: serverAddrs, + }, + } + } +} diff --git a/agent/grpc-external/services/peerstream/subscription_manager_test.go b/agent/grpc-external/services/peerstream/subscription_manager_test.go index 03b89dbcc..d81568f0a 100644 --- a/agent/grpc-external/services/peerstream/subscription_manager_test.go +++ b/agent/grpc-external/services/peerstream/subscription_manager_test.go @@ -3,14 +3,17 @@ package peerstream import ( "context" "sort" + "sync" "testing" "time" + "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" "github.com/hashicorp/consul/acl" "github.com/hashicorp/consul/agent/cache" "github.com/hashicorp/consul/agent/connect" + "github.com/hashicorp/consul/agent/consul/autopilotevents" "github.com/hashicorp/consul/agent/consul/state" "github.com/hashicorp/consul/agent/consul/stream" "github.com/hashicorp/consul/agent/structs" @@ -627,20 +630,100 @@ func TestSubscriptionManager_CARoots(t *testing.T) { }) } +func TestSubscriptionManager_ServerAddrs(t *testing.T) { + backend := newTestSubscriptionBackend(t) + + ctx, cancel := context.WithCancel(context.Background()) + t.Cleanup(cancel) + + // Create a peering + _, id := backend.ensurePeering(t, "my-peering") + partition := acl.DefaultEnterpriseMeta().PartitionOrEmpty() + + payload := autopilotevents.EventPayloadReadyServers{ + autopilotevents.ReadyServerInfo{ + ID: "9aeb73f6-e83e-43c1-bdc9-ca5e43efe3e4", + Address: "198.18.0.1", + Version: "1.13.1", + ExtGRPCPort: 8502, + }, + } + // mock handler only gets called once during the initial subscription + backend.handler.expect("", 0, 1, payload) + + // Only configure a tracker for server address events. + tracker := newResourceSubscriptionTracker() + tracker.Subscribe(pbpeerstream.TypeURLPeeringServerAddresses) + + mgr := newSubscriptionManager(ctx, + testutil.Logger(t), + Config{ + Datacenter: "dc1", + ConnectEnabled: true, + }, + connect.TestTrustDomain, + backend, + func() StateStore { + return backend.store + }, + tracker) + subCh := mgr.subscribe(ctx, id, "my-peering", partition) + + testutil.RunStep(t, "initial events", func(t *testing.T) { + expectEvents(t, subCh, + func(t *testing.T, got cache.UpdateEvent) { + require.Equal(t, subServerAddrs, got.CorrelationID) + addrs, ok := got.Result.(*pbpeering.PeeringServerAddresses) + require.True(t, ok) + + require.Equal(t, []string{"198.18.0.1:8502"}, addrs.GetAddresses()) + }, + ) + }) + + testutil.RunStep(t, "added server", func(t *testing.T) { + payload = append(payload, autopilotevents.ReadyServerInfo{ + ID: "eec8721f-c42b-48da-a5a5-07565158015e", + Address: "198.18.0.2", + Version: "1.13.1", + ExtGRPCPort: 9502, + }) + backend.Publish([]stream.Event{ + { + Topic: autopilotevents.EventTopicReadyServers, + Index: 2, + Payload: payload, + }, + }) + + expectEvents(t, subCh, + func(t *testing.T, got cache.UpdateEvent) { + require.Equal(t, subServerAddrs, got.CorrelationID) + addrs, ok := got.Result.(*pbpeering.PeeringServerAddresses) + require.True(t, ok) + + require.Equal(t, []string{"198.18.0.1:8502", "198.18.0.2:9502"}, addrs.GetAddresses()) + }, + ) + }) +} + type testSubscriptionBackend struct { state.EventPublisher - store *state.Store + store *state.Store + handler *mockSnapshotHandler lastIdx uint64 } func newTestSubscriptionBackend(t *testing.T) *testSubscriptionBackend { publisher := stream.NewEventPublisher(10 * time.Second) - store := newStateStore(t, publisher) + store, handler := newStateStore(t, publisher) backend := &testSubscriptionBackend{ EventPublisher: publisher, store: store, + handler: handler, } backend.ensureCAConfig(t, &structs.CAConfiguration{ @@ -739,20 +822,35 @@ func setupTestPeering(t *testing.T, store *state.Store, name string, index uint6 return p.ID } -func newStateStore(t *testing.T, publisher *stream.EventPublisher) *state.Store { - ctx, cancel := context.WithCancel(context.Background()) - t.Cleanup(cancel) - +func newStateStore(t *testing.T, publisher *stream.EventPublisher) (*state.Store, *mockSnapshotHandler) { gc, err := state.NewTombstoneGC(time.Second, time.Millisecond) require.NoError(t, err) + handler := newMockSnapshotHandler(t) + store := state.NewStateStoreWithEventPublisher(gc, publisher) require.NoError(t, publisher.RegisterHandler(state.EventTopicServiceHealth, store.ServiceHealthSnapshot, false)) require.NoError(t, publisher.RegisterHandler(state.EventTopicServiceHealthConnect, store.ServiceHealthSnapshot, false)) require.NoError(t, publisher.RegisterHandler(state.EventTopicCARoots, store.CARootsSnapshot, false)) - go publisher.Run(ctx) + require.NoError(t, publisher.RegisterHandler(autopilotevents.EventTopicReadyServers, handler.handle, false)) - return store + // WaitGroup used to make sure that the publisher returns + // before handler's t.Cleanup is called (otherwise an event + // might fire during an assertion and cause a data race). + var wg sync.WaitGroup + ctx, cancel := context.WithCancel(context.Background()) + t.Cleanup(func() { + cancel() + wg.Wait() + }) + + wg.Add(1) + go func() { + publisher.Run(ctx) + wg.Done() + }() + + return store, handler } func expectEvents( @@ -870,3 +968,39 @@ func pbCheck(node, svcID, svcName, status string, entMeta *pbcommon.EnterpriseMe EnterpriseMeta: entMeta, } } + +// mockSnapshotHandler is copied from server_discovery/server_test.go +type mockSnapshotHandler struct { + mock.Mock +} + +func newMockSnapshotHandler(t *testing.T) *mockSnapshotHandler { + handler := &mockSnapshotHandler{} + t.Cleanup(func() { + handler.AssertExpectations(t) + }) + return handler +} + +func (m *mockSnapshotHandler) handle(req stream.SubscribeRequest, buf stream.SnapshotAppender) (uint64, error) { + ret := m.Called(req, buf) + return ret.Get(0).(uint64), ret.Error(1) +} + +func (m *mockSnapshotHandler) expect(token string, requestIndex uint64, eventIndex uint64, payload autopilotevents.EventPayloadReadyServers) { + m.On("handle", stream.SubscribeRequest{ + Topic: autopilotevents.EventTopicReadyServers, + Subject: stream.SubjectNone, + Token: token, + Index: requestIndex, + }, mock.Anything).Run(func(args mock.Arguments) { + buf := args.Get(1).(stream.SnapshotAppender) + buf.Append([]stream.Event{ + { + Topic: autopilotevents.EventTopicReadyServers, + Index: eventIndex, + Payload: payload, + }, + }) + }).Return(eventIndex, nil) +} diff --git a/agent/grpc-external/services/peerstream/subscription_state.go b/agent/grpc-external/services/peerstream/subscription_state.go index 58e631f70..9e32be545 100644 --- a/agent/grpc-external/services/peerstream/subscription_state.go +++ b/agent/grpc-external/services/peerstream/subscription_state.go @@ -93,6 +93,9 @@ func (s *subscriptionState) cleanupEventVersions(logger hclog.Logger) { case id == caRootsPayloadID: keep = true + case id == serverAddrsPayloadID: + keep = true + case strings.HasPrefix(id, servicePayloadIDPrefix): name := strings.TrimPrefix(id, servicePayloadIDPrefix) sn := structs.ServiceNameFromString(name) @@ -129,6 +132,7 @@ type pendingEvent struct { } const ( + serverAddrsPayloadID = "server-addrs" caRootsPayloadID = "roots" meshGatewayPayloadID = "mesh-gateway" servicePayloadIDPrefix = "service:" diff --git a/agent/http.go b/agent/http.go index 98beb6feb..ff7764f0d 100644 --- a/agent/http.go +++ b/agent/http.go @@ -81,6 +81,10 @@ type HTTPHandlers struct { configReloaders []ConfigReloader h http.Handler metricsProxyCfg atomic.Value + + // proxyTransport is used by UIMetricsProxy to keep + // a managed pool of connections. + proxyTransport http.RoundTripper } // endpoint is a Consul-specific HTTP handler that takes the usual arguments in diff --git a/agent/proxycfg-glue/config_entry.go b/agent/proxycfg-glue/config_entry.go index 1f6fbf245..1321e352a 100644 --- a/agent/proxycfg-glue/config_entry.go +++ b/agent/proxycfg-glue/config_entry.go @@ -4,11 +4,9 @@ import ( "context" "fmt" - "github.com/hashicorp/go-hclog" - "github.com/hashicorp/consul/acl" "github.com/hashicorp/consul/agent/cache" - "github.com/hashicorp/consul/agent/consul/stream" + cachetype "github.com/hashicorp/consul/agent/cache-types" "github.com/hashicorp/consul/agent/proxycfg" "github.com/hashicorp/consul/agent/structs" "github.com/hashicorp/consul/agent/submatview" @@ -17,15 +15,16 @@ import ( "github.com/hashicorp/consul/proto/pbsubscribe" ) -// ServerDataSourceDeps contains the dependencies needed for sourcing data from -// server-local sources (e.g. materialized views). -type ServerDataSourceDeps struct { - Datacenter string - ViewStore *submatview.Store - EventPublisher *stream.EventPublisher - Logger hclog.Logger - ACLResolver submatview.ACLResolver - GetStore func() Store +// CacheConfigEntry satisfies the proxycfg.ConfigEntry interface by sourcing +// data from the agent cache. +func CacheConfigEntry(c *cache.Cache) proxycfg.ConfigEntry { + return &cacheProxyDataSource[*structs.ConfigEntryQuery]{c, cachetype.ConfigEntryName} +} + +// CacheConfigEntryList satisfies the proxycfg.ConfigEntryList interface by +// sourcing data from the agent cache. +func CacheConfigEntryList(c *cache.Cache) proxycfg.ConfigEntryList { + return &cacheProxyDataSource[*structs.ConfigEntryQuery]{c, cachetype.ConfigEntryListName} } // ServerConfigEntry satisfies the proxycfg.ConfigEntry interface by sourcing diff --git a/agent/proxycfg-glue/glue.go b/agent/proxycfg-glue/glue.go index 1b22b02bd..6aef1da54 100644 --- a/agent/proxycfg-glue/glue.go +++ b/agent/proxycfg-glue/glue.go @@ -3,20 +3,35 @@ package proxycfgglue import ( "context" - "github.com/hashicorp/consul/proto/pbpeering" + "github.com/hashicorp/go-hclog" "github.com/hashicorp/go-memdb" + "github.com/hashicorp/consul/proto/pbpeering" + "github.com/hashicorp/consul/acl" "github.com/hashicorp/consul/agent/cache" cachetype "github.com/hashicorp/consul/agent/cache-types" "github.com/hashicorp/consul/agent/configentry" "github.com/hashicorp/consul/agent/consul/discoverychain" "github.com/hashicorp/consul/agent/consul/state" + "github.com/hashicorp/consul/agent/consul/stream" "github.com/hashicorp/consul/agent/consul/watch" "github.com/hashicorp/consul/agent/proxycfg" "github.com/hashicorp/consul/agent/structs" + "github.com/hashicorp/consul/agent/submatview" ) +// ServerDataSourceDeps contains the dependencies needed for sourcing data from +// server-local sources (e.g. materialized views). +type ServerDataSourceDeps struct { + Datacenter string + ViewStore *submatview.Store + EventPublisher *stream.EventPublisher + Logger hclog.Logger + ACLResolver submatview.ACLResolver + GetStore func() Store +} + // Store is the state store interface required for server-local data sources. type Store interface { watch.StateStore @@ -25,7 +40,9 @@ type Store interface { FederationStateList(ws memdb.WatchSet) (uint64, []*structs.FederationState, error) GatewayServices(ws memdb.WatchSet, gateway string, entMeta *acl.EnterpriseMeta) (uint64, structs.GatewayServices, error) IntentionTopology(ws memdb.WatchSet, target structs.ServiceName, downstreams bool, defaultDecision acl.EnforcementDecision, intentionTarget structs.IntentionTargetType) (uint64, structs.ServiceList, error) + ReadResolvedServiceConfigEntries(ws memdb.WatchSet, serviceName string, entMeta *acl.EnterpriseMeta, upstreamIDs []structs.ServiceID, proxyMode structs.ProxyMode) (uint64, *configentry.ResolvedServiceConfigSet, error) ServiceDiscoveryChain(ws memdb.WatchSet, serviceName string, entMeta *acl.EnterpriseMeta, req discoverychain.CompileRequest) (uint64, *structs.CompiledDiscoveryChain, *configentry.DiscoveryChainSet, error) + ServiceDump(ws memdb.WatchSet, kind structs.ServiceKind, useKind bool, entMeta *acl.EnterpriseMeta, peerName string) (uint64, structs.CheckServiceNodes, error) PeeringTrustBundleRead(ws memdb.WatchSet, q state.Query) (uint64, *pbpeering.PeeringTrustBundle, error) PeeringTrustBundleList(ws memdb.WatchSet, entMeta acl.EnterpriseMeta) (uint64, []*pbpeering.PeeringTrustBundle, error) TrustBundleListByService(ws memdb.WatchSet, service, dc string, entMeta acl.EnterpriseMeta) (uint64, []*pbpeering.PeeringTrustBundle, error) @@ -34,24 +51,18 @@ type Store interface { // CacheCARoots satisfies the proxycfg.CARoots interface by sourcing data from // the agent cache. +// +// Note: there isn't a server-local equivalent of this data source because +// "agentless" proxies obtain certificates via SDS served by consul-dataplane. func CacheCARoots(c *cache.Cache) proxycfg.CARoots { return &cacheProxyDataSource[*structs.DCSpecificRequest]{c, cachetype.ConnectCARootName} } -// CacheConfigEntry satisfies the proxycfg.ConfigEntry interface by sourcing -// data from the agent cache. -func CacheConfigEntry(c *cache.Cache) proxycfg.ConfigEntry { - return &cacheProxyDataSource[*structs.ConfigEntryQuery]{c, cachetype.ConfigEntryName} -} - -// CacheConfigEntryList satisfies the proxycfg.ConfigEntryList interface by -// sourcing data from the agent cache. -func CacheConfigEntryList(c *cache.Cache) proxycfg.ConfigEntryList { - return &cacheProxyDataSource[*structs.ConfigEntryQuery]{c, cachetype.ConfigEntryListName} -} - // CacheDatacenters satisfies the proxycfg.Datacenters interface by sourcing // data from the agent cache. +// +// Note: there isn't a server-local equivalent of this data source because it +// relies on polling (so a more efficient method isn't available). func CacheDatacenters(c *cache.Cache) proxycfg.Datacenters { return &cacheProxyDataSource[*structs.DatacentersRequest]{c, cachetype.CatalogDatacentersName} } @@ -64,46 +75,31 @@ func CacheServiceGateways(c *cache.Cache) proxycfg.GatewayServices { // CacheHTTPChecks satisifies the proxycfg.HTTPChecks interface by sourcing // data from the agent cache. +// +// Note: there isn't a server-local equivalent of this data source because only +// services registered to the local agent can be health checked by it. func CacheHTTPChecks(c *cache.Cache) proxycfg.HTTPChecks { return &cacheProxyDataSource[*cachetype.ServiceHTTPChecksRequest]{c, cachetype.ServiceHTTPChecksName} } -// CacheIntentionUpstreams satisfies the proxycfg.IntentionUpstreams interface -// by sourcing data from the agent cache. -func CacheIntentionUpstreams(c *cache.Cache) proxycfg.IntentionUpstreams { - return &cacheProxyDataSource[*structs.ServiceSpecificRequest]{c, cachetype.IntentionUpstreamsName} -} - -// CacheIntentionUpstreamsDestination satisfies the proxycfg.IntentionUpstreamsDestination interface -// by sourcing data from the agent cache. -func CacheIntentionUpstreamsDestination(c *cache.Cache) proxycfg.IntentionUpstreams { - return &cacheProxyDataSource[*structs.ServiceSpecificRequest]{c, cachetype.IntentionUpstreamsDestinationName} -} - -// CacheInternalServiceDump satisfies the proxycfg.InternalServiceDump -// interface by sourcing data from the agent cache. -func CacheInternalServiceDump(c *cache.Cache) proxycfg.InternalServiceDump { - return &cacheProxyDataSource[*structs.ServiceDumpRequest]{c, cachetype.InternalServiceDumpName} -} - // CacheLeafCertificate satisifies the proxycfg.LeafCertificate interface by // sourcing data from the agent cache. +// +// Note: there isn't a server-local equivalent of this data source because +// "agentless" proxies obtain certificates via SDS served by consul-dataplane. func CacheLeafCertificate(c *cache.Cache) proxycfg.LeafCertificate { return &cacheProxyDataSource[*cachetype.ConnectCALeafRequest]{c, cachetype.ConnectCALeafName} } // CachePrepraredQuery satisfies the proxycfg.PreparedQuery interface by // sourcing data from the agent cache. +// +// Note: there isn't a server-local equivalent of this data source because it +// relies on polling (so a more efficient method isn't available). func CachePrepraredQuery(c *cache.Cache) proxycfg.PreparedQuery { return &cacheProxyDataSource[*structs.PreparedQueryExecuteRequest]{c, cachetype.PreparedQueryName} } -// CacheResolvedServiceConfig satisfies the proxycfg.ResolvedServiceConfig -// interface by sourcing data from the agent cache. -func CacheResolvedServiceConfig(c *cache.Cache) proxycfg.ResolvedServiceConfig { - return &cacheProxyDataSource[*structs.ServiceConfigRequest]{c, cachetype.ResolvedServiceConfigName} -} - // cacheProxyDataSource implements a generic wrapper around the agent cache to // provide data to the proxycfg.Manager. type cacheProxyDataSource[ReqType cache.Request] struct { @@ -131,6 +127,15 @@ func dispatchCacheUpdate(ch chan<- proxycfg.UpdateEvent) cache.Callback { } } +func dispatchBlockingQueryUpdate[ResultType any](ch chan<- proxycfg.UpdateEvent) func(context.Context, string, ResultType, error) { + return func(ctx context.Context, correlationID string, result ResultType, err error) { + select { + case ch <- newUpdateEvent(correlationID, result, err): + case <-ctx.Done(): + } + } +} + func newUpdateEvent(correlationID string, result any, err error) proxycfg.UpdateEvent { // This roughly matches the logic in agent/submatview.LocalMaterializer.isTerminalError. if acl.IsErrNotFound(err) { diff --git a/agent/proxycfg-glue/intention_upstreams.go b/agent/proxycfg-glue/intention_upstreams.go index a694d033b..e5d5e9959 100644 --- a/agent/proxycfg-glue/intention_upstreams.go +++ b/agent/proxycfg-glue/intention_upstreams.go @@ -5,20 +5,45 @@ import ( "github.com/hashicorp/go-memdb" + "github.com/hashicorp/consul/agent/cache" + cachetype "github.com/hashicorp/consul/agent/cache-types" "github.com/hashicorp/consul/agent/consul/watch" "github.com/hashicorp/consul/agent/proxycfg" "github.com/hashicorp/consul/agent/structs" "github.com/hashicorp/consul/agent/structs/aclfilter" ) +// CacheIntentionUpstreams satisfies the proxycfg.IntentionUpstreams interface +// by sourcing upstreams for the given service, inferred from intentions, from +// the agent cache. +func CacheIntentionUpstreams(c *cache.Cache) proxycfg.IntentionUpstreams { + return &cacheProxyDataSource[*structs.ServiceSpecificRequest]{c, cachetype.IntentionUpstreamsName} +} + +// CacheIntentionUpstreamsDestination satisfies the proxycfg.IntentionUpstreams +// interface by sourcing upstreams for the given destination, inferred from +// intentions, from the agent cache. +func CacheIntentionUpstreamsDestination(c *cache.Cache) proxycfg.IntentionUpstreams { + return &cacheProxyDataSource[*structs.ServiceSpecificRequest]{c, cachetype.IntentionUpstreamsDestinationName} +} + // ServerIntentionUpstreams satisfies the proxycfg.IntentionUpstreams interface -// by sourcing data from a blocking query against the server's state store. +// by sourcing upstreams for the given service, inferred from intentions, from +// the server's state store. func ServerIntentionUpstreams(deps ServerDataSourceDeps) proxycfg.IntentionUpstreams { - return serverIntentionUpstreams{deps} + return serverIntentionUpstreams{deps, structs.IntentionTargetService} +} + +// ServerIntentionUpstreamsDestination satisfies the proxycfg.IntentionUpstreams +// interface by sourcing upstreams for the given destination, inferred from +// intentions, from the server's state store. +func ServerIntentionUpstreamsDestination(deps ServerDataSourceDeps) proxycfg.IntentionUpstreams { + return serverIntentionUpstreams{deps, structs.IntentionTargetDestination} } type serverIntentionUpstreams struct { - deps ServerDataSourceDeps + deps ServerDataSourceDeps + target structs.IntentionTargetType } func (s serverIntentionUpstreams) Notify(ctx context.Context, req *structs.ServiceSpecificRequest, correlationID string, ch chan<- proxycfg.UpdateEvent) error { @@ -32,7 +57,7 @@ func (s serverIntentionUpstreams) Notify(ctx context.Context, req *structs.Servi } defaultDecision := authz.IntentionDefaultAllow(nil) - index, services, err := store.IntentionTopology(ws, target, false, defaultDecision, structs.IntentionTargetService) + index, services, err := store.IntentionTopology(ws, target, false, defaultDecision, s.target) if err != nil { return 0, nil, err } @@ -51,12 +76,3 @@ func (s serverIntentionUpstreams) Notify(ctx context.Context, req *structs.Servi dispatchBlockingQueryUpdate[*structs.IndexedServiceList](ch), ) } - -func dispatchBlockingQueryUpdate[ResultType any](ch chan<- proxycfg.UpdateEvent) func(context.Context, string, ResultType, error) { - return func(ctx context.Context, correlationID string, result ResultType, err error) { - select { - case ch <- newUpdateEvent(correlationID, result, err): - case <-ctx.Done(): - } - } -} diff --git a/agent/proxycfg-glue/internal_service_dump.go b/agent/proxycfg-glue/internal_service_dump.go new file mode 100644 index 000000000..2d94487f3 --- /dev/null +++ b/agent/proxycfg-glue/internal_service_dump.go @@ -0,0 +1,99 @@ +package proxycfgglue + +import ( + "context" + "fmt" + + "github.com/hashicorp/go-bexpr" + "github.com/hashicorp/go-memdb" + + "github.com/hashicorp/consul/agent/cache" + cachetype "github.com/hashicorp/consul/agent/cache-types" + "github.com/hashicorp/consul/agent/consul/watch" + "github.com/hashicorp/consul/agent/proxycfg" + "github.com/hashicorp/consul/agent/structs" + "github.com/hashicorp/consul/agent/structs/aclfilter" +) + +// CacheInternalServiceDump satisfies the proxycfg.InternalServiceDump +// interface by sourcing data from the agent cache. +func CacheInternalServiceDump(c *cache.Cache) proxycfg.InternalServiceDump { + return &cacheInternalServiceDump{c} +} + +// cacheInternalServiceDump wraps the underlying cache-type to return a simpler +// subset of the response (as this is all we use in proxycfg). +type cacheInternalServiceDump struct { + c *cache.Cache +} + +func (c *cacheInternalServiceDump) Notify(ctx context.Context, req *structs.ServiceDumpRequest, correlationID string, ch chan<- proxycfg.UpdateEvent) error { + dispatch := dispatchCacheUpdate(ch) + + return c.c.NotifyCallback(ctx, cachetype.InternalServiceDumpName, req, correlationID, + func(ctx context.Context, event cache.UpdateEvent) { + if r, _ := event.Result.(*structs.IndexedNodesWithGateways); r != nil { + event.Result = &structs.IndexedCheckServiceNodes{ + Nodes: r.Nodes, + QueryMeta: r.QueryMeta, + } + } + dispatch(ctx, event) + }) +} + +// ServerInternalServiceDump satisfies the proxycfg.InternalServiceDump +// interface by sourcing data from a blocking query against the server's +// state store. +func ServerInternalServiceDump(deps ServerDataSourceDeps, remoteSource proxycfg.InternalServiceDump) proxycfg.InternalServiceDump { + return &serverInternalServiceDump{deps, remoteSource} +} + +type serverInternalServiceDump struct { + deps ServerDataSourceDeps + remoteSource proxycfg.InternalServiceDump +} + +func (s *serverInternalServiceDump) Notify(ctx context.Context, req *structs.ServiceDumpRequest, correlationID string, ch chan<- proxycfg.UpdateEvent) error { + if req.Datacenter != s.deps.Datacenter { + return s.remoteSource.Notify(ctx, req, correlationID, ch) + } + + filter, err := bexpr.CreateFilter(req.Filter, nil, structs.CheckServiceNodes{}) + if err != nil { + return err + } + + // This is just the small subset of the Internal.ServiceDump RPC handler used + // by proxycfg. + return watch.ServerLocalNotify(ctx, correlationID, s.deps.GetStore, + func(ws memdb.WatchSet, store Store) (uint64, *structs.IndexedCheckServiceNodes, error) { + authz, err := s.deps.ACLResolver.ResolveTokenAndDefaultMeta(req.Token, &req.EnterpriseMeta, nil) + if err != nil { + return 0, nil, err + } + + idx, nodes, err := store.ServiceDump(ws, req.ServiceKind, req.UseServiceKind, &req.EnterpriseMeta, structs.DefaultPeerKeyword) + if err != nil { + return 0, nil, err + } + + raw, err := filter.Execute(nodes) + if err != nil { + return 0, nil, fmt.Errorf("could not filter local service dump: %w", err) + } + nodes = raw.(structs.CheckServiceNodes) + + aclfilter.New(authz, s.deps.Logger).Filter(&nodes) + + return idx, &structs.IndexedCheckServiceNodes{ + Nodes: nodes, + QueryMeta: structs.QueryMeta{ + Index: idx, + Backend: structs.QueryBackendBlocking, + }, + }, nil + }, + dispatchBlockingQueryUpdate[*structs.IndexedCheckServiceNodes](ch), + ) +} diff --git a/agent/proxycfg-glue/internal_service_dump_test.go b/agent/proxycfg-glue/internal_service_dump_test.go new file mode 100644 index 000000000..66245c52a --- /dev/null +++ b/agent/proxycfg-glue/internal_service_dump_test.go @@ -0,0 +1,139 @@ +package proxycfgglue + +import ( + "context" + "errors" + "fmt" + "testing" + + "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/require" + + "github.com/hashicorp/consul/acl" + "github.com/hashicorp/consul/agent/consul/state" + "github.com/hashicorp/consul/agent/proxycfg" + "github.com/hashicorp/consul/agent/structs" +) + +func TestServerInternalServiceDump(t *testing.T) { + t.Run("remote queries are delegated to the remote source", func(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + t.Cleanup(cancel) + + var ( + req = &structs.ServiceDumpRequest{Datacenter: "dc2"} + correlationID = "correlation-id" + ch = make(chan<- proxycfg.UpdateEvent) + result = errors.New("KABOOM") + ) + + remoteSource := newMockInternalServiceDump(t) + remoteSource.On("Notify", ctx, req, correlationID, ch).Return(result) + + dataSource := ServerInternalServiceDump(ServerDataSourceDeps{Datacenter: "dc1"}, remoteSource) + err := dataSource.Notify(ctx, req, correlationID, ch) + require.Equal(t, result, err) + }) + + t.Run("local queries are served from the state store", func(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + t.Cleanup(cancel) + + nextIndex := indexGenerator() + + store := state.NewStateStore(nil) + + services := []*structs.NodeService{ + { + Service: "mgw", + Kind: structs.ServiceKindMeshGateway, + }, + { + Service: "web", + Kind: structs.ServiceKindTypical, + }, + { + Service: "db", + Kind: structs.ServiceKindTypical, + }, + } + for idx, service := range services { + require.NoError(t, store.EnsureRegistration(nextIndex(), &structs.RegisterRequest{ + Node: fmt.Sprintf("node-%d", idx), + Service: service, + })) + } + + authz := newStaticResolver( + policyAuthorizer(t, ` + service "mgw" { policy = "read" } + service "web" { policy = "read" } + service "db" { policy = "read" } + node_prefix "node-" { policy = "read" } + `), + ) + + dataSource := ServerInternalServiceDump(ServerDataSourceDeps{ + GetStore: func() Store { return store }, + ACLResolver: authz, + }, nil) + + t.Run("filter by kind", func(t *testing.T) { + eventCh := make(chan proxycfg.UpdateEvent) + require.NoError(t, dataSource.Notify(ctx, &structs.ServiceDumpRequest{ + ServiceKind: structs.ServiceKindMeshGateway, + UseServiceKind: true, + }, "", eventCh)) + + result := getEventResult[*structs.IndexedCheckServiceNodes](t, eventCh) + require.Len(t, result.Nodes, 1) + require.Equal(t, "mgw", result.Nodes[0].Service.Service) + }) + + t.Run("bexpr filtering", func(t *testing.T) { + eventCh := make(chan proxycfg.UpdateEvent) + require.NoError(t, dataSource.Notify(ctx, &structs.ServiceDumpRequest{ + QueryOptions: structs.QueryOptions{Filter: `Service.Service == "web"`}, + }, "", eventCh)) + + result := getEventResult[*structs.IndexedCheckServiceNodes](t, eventCh) + require.Len(t, result.Nodes, 1) + require.Equal(t, "web", result.Nodes[0].Service.Service) + }) + + t.Run("all services", func(t *testing.T) { + eventCh := make(chan proxycfg.UpdateEvent) + require.NoError(t, dataSource.Notify(ctx, &structs.ServiceDumpRequest{}, "", eventCh)) + + result := getEventResult[*structs.IndexedCheckServiceNodes](t, eventCh) + require.Len(t, result.Nodes, 3) + }) + + t.Run("access denied", func(t *testing.T) { + authz.SwapAuthorizer(acl.DenyAll()) + + eventCh := make(chan proxycfg.UpdateEvent) + require.NoError(t, dataSource.Notify(ctx, &structs.ServiceDumpRequest{}, "", eventCh)) + + result := getEventResult[*structs.IndexedCheckServiceNodes](t, eventCh) + require.Empty(t, result.Nodes) + }) + }) +} + +func newMockInternalServiceDump(t *testing.T) *mockInternalServiceDump { + mock := &mockInternalServiceDump{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} + +type mockInternalServiceDump struct { + mock.Mock +} + +func (m *mockInternalServiceDump) Notify(ctx context.Context, req *structs.ServiceDumpRequest, correlationID string, ch chan<- proxycfg.UpdateEvent) error { + return m.Called(ctx, req, correlationID, ch).Error(0) +} diff --git a/agent/proxycfg-glue/resolved_service_config.go b/agent/proxycfg-glue/resolved_service_config.go new file mode 100644 index 000000000..3cd952e90 --- /dev/null +++ b/agent/proxycfg-glue/resolved_service_config.go @@ -0,0 +1,70 @@ +package proxycfgglue + +import ( + "context" + "errors" + + "github.com/hashicorp/go-memdb" + + "github.com/hashicorp/consul/agent/cache" + cachetype "github.com/hashicorp/consul/agent/cache-types" + "github.com/hashicorp/consul/agent/configentry" + "github.com/hashicorp/consul/agent/consul/watch" + "github.com/hashicorp/consul/agent/proxycfg" + "github.com/hashicorp/consul/agent/structs" +) + +// CacheResolvedServiceConfig satisfies the proxycfg.ResolvedServiceConfig +// interface by sourcing data from the agent cache. +func CacheResolvedServiceConfig(c *cache.Cache) proxycfg.ResolvedServiceConfig { + return &cacheProxyDataSource[*structs.ServiceConfigRequest]{c, cachetype.ResolvedServiceConfigName} +} + +// ServerResolvedServiceConfig satisfies the proxycfg.ResolvedServiceConfig +// interface by sourcing data from a blocking query against the server's state +// store. +func ServerResolvedServiceConfig(deps ServerDataSourceDeps, remoteSource proxycfg.ResolvedServiceConfig) proxycfg.ResolvedServiceConfig { + return &serverResolvedServiceConfig{deps, remoteSource} +} + +type serverResolvedServiceConfig struct { + deps ServerDataSourceDeps + remoteSource proxycfg.ResolvedServiceConfig +} + +func (s *serverResolvedServiceConfig) Notify(ctx context.Context, req *structs.ServiceConfigRequest, correlationID string, ch chan<- proxycfg.UpdateEvent) error { + if req.Datacenter != s.deps.Datacenter { + return s.remoteSource.Notify(ctx, req, correlationID, ch) + } + + if len(req.Upstreams) != 0 { + return errors.New("ServerResolvedServiceConfig does not support the legacy Upstreams parameter") + } + + return watch.ServerLocalNotify(ctx, correlationID, s.deps.GetStore, + func(ws memdb.WatchSet, store Store) (uint64, *structs.ServiceConfigResponse, error) { + authz, err := s.deps.ACLResolver.ResolveTokenAndDefaultMeta(req.Token, &req.EnterpriseMeta, nil) + if err != nil { + return 0, nil, err + } + + if err := authz.ToAllowAuthorizer().ServiceReadAllowed(req.Name, nil); err != nil { + return 0, nil, err + } + + idx, entries, err := store.ReadResolvedServiceConfigEntries(ws, req.Name, &req.EnterpriseMeta, req.UpstreamIDs, req.Mode) + if err != nil { + return 0, nil, err + } + + reply, err := configentry.ComputeResolvedServiceConfig(req, req.UpstreamIDs, false, entries, s.deps.Logger) + if err != nil { + return 0, nil, err + } + reply.Index = idx + + return idx, reply, nil + }, + dispatchBlockingQueryUpdate[*structs.ServiceConfigResponse](ch), + ) +} diff --git a/agent/proxycfg-glue/resolved_service_config_test.go b/agent/proxycfg-glue/resolved_service_config_test.go new file mode 100644 index 000000000..3f165aa55 --- /dev/null +++ b/agent/proxycfg-glue/resolved_service_config_test.go @@ -0,0 +1,116 @@ +package proxycfgglue + +import ( + "context" + "errors" + "fmt" + "testing" + + "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/require" + + "github.com/hashicorp/consul/acl" + "github.com/hashicorp/consul/agent/consul/state" + "github.com/hashicorp/consul/agent/proxycfg" + "github.com/hashicorp/consul/agent/structs" + "github.com/hashicorp/consul/sdk/testutil" +) + +func TestServerResolvedServiceConfig(t *testing.T) { + t.Run("remote queries are delegated to the remote source", func(t *testing.T) { + var ( + ctx = context.Background() + req = &structs.ServiceConfigRequest{Datacenter: "dc2"} + correlationID = "correlation-id" + ch = make(chan<- proxycfg.UpdateEvent) + result = errors.New("KABOOM") + ) + + remoteSource := newMockResolvedServiceConfig(t) + remoteSource.On("Notify", ctx, req, correlationID, ch).Return(result) + + dataSource := ServerResolvedServiceConfig(ServerDataSourceDeps{Datacenter: "dc1"}, remoteSource) + err := dataSource.Notify(ctx, req, correlationID, ch) + require.Equal(t, result, err) + }) + + t.Run("local queries are served from the state store", func(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + t.Cleanup(cancel) + + const ( + serviceName = "web" + datacenter = "dc1" + ) + + store := state.NewStateStore(nil) + nextIndex := indexGenerator() + + require.NoError(t, store.EnsureConfigEntry(nextIndex(), &structs.ServiceConfigEntry{ + Name: serviceName, + Protocol: "http", + })) + + authz := newStaticResolver( + policyAuthorizer(t, fmt.Sprintf(`service "%s" { policy = "read" }`, serviceName)), + ) + + dataSource := ServerResolvedServiceConfig(ServerDataSourceDeps{ + Datacenter: datacenter, + ACLResolver: authz, + GetStore: func() Store { return store }, + }, nil) + + eventCh := make(chan proxycfg.UpdateEvent) + require.NoError(t, dataSource.Notify(ctx, &structs.ServiceConfigRequest{Datacenter: datacenter, Name: serviceName}, "", eventCh)) + + testutil.RunStep(t, "initial state", func(t *testing.T) { + result := getEventResult[*structs.ServiceConfigResponse](t, eventCh) + require.Equal(t, map[string]any{"protocol": "http"}, result.ProxyConfig) + }) + + testutil.RunStep(t, "write proxy defaults", func(t *testing.T) { + require.NoError(t, store.EnsureConfigEntry(nextIndex(), &structs.ProxyConfigEntry{ + Name: structs.ProxyConfigGlobal, + Mode: structs.ProxyModeDirect, + })) + result := getEventResult[*structs.ServiceConfigResponse](t, eventCh) + require.Equal(t, structs.ProxyModeDirect, result.Mode) + }) + + testutil.RunStep(t, "delete service config", func(t *testing.T) { + require.NoError(t, store.DeleteConfigEntry(nextIndex(), structs.ServiceDefaults, serviceName, nil)) + + result := getEventResult[*structs.ServiceConfigResponse](t, eventCh) + require.Empty(t, result.ProxyConfig) + }) + + testutil.RunStep(t, "revoke access", func(t *testing.T) { + authz.SwapAuthorizer(acl.DenyAll()) + + require.NoError(t, store.EnsureConfigEntry(nextIndex(), &structs.ServiceConfigEntry{ + Name: serviceName, + Protocol: "http", + })) + + expectNoEvent(t, eventCh) + }) + }) +} + +func newMockResolvedServiceConfig(t *testing.T) *mockResolvedServiceConfig { + mock := &mockResolvedServiceConfig{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} + +type mockResolvedServiceConfig struct { + mock.Mock +} + +func (m *mockResolvedServiceConfig) Notify(ctx context.Context, req *structs.ServiceConfigRequest, correlationID string, ch chan<- proxycfg.UpdateEvent) error { + return m.Called(ctx, req, correlationID, ch).Error(0) +} diff --git a/agent/proxycfg/data_sources.go b/agent/proxycfg/data_sources.go index 3649bed2d..c01261071 100644 --- a/agent/proxycfg/data_sources.go +++ b/agent/proxycfg/data_sources.go @@ -89,10 +89,10 @@ type DataSources struct { // IntentionUpstreamsDestination provides intention-inferred upstream updates on a // notification channel. - IntentionUpstreamsDestination IntentionUpstreamsDestination + IntentionUpstreamsDestination IntentionUpstreams - // InternalServiceDump provides updates about a (gateway) service on a - // notification channel. + // InternalServiceDump provides updates about services of a given kind (e.g. + // mesh gateways) on a notification channel. InternalServiceDump InternalServiceDump // LeafCertificate provides updates about the service's leaf certificate on a @@ -197,14 +197,8 @@ type IntentionUpstreams interface { Notify(ctx context.Context, req *structs.ServiceSpecificRequest, correlationID string, ch chan<- UpdateEvent) error } -// IntentionUpstreamsDestination is the interface used to consume updates about upstreams destination -// inferred from service intentions. -type IntentionUpstreamsDestination interface { - Notify(ctx context.Context, req *structs.ServiceSpecificRequest, correlationID string, ch chan<- UpdateEvent) error -} - -// InternalServiceDump is the interface used to consume updates about a (gateway) -// service via the internal ServiceDump RPC. +// InternalServiceDump is the interface used to consume updates about services +// of a given kind (e.g. mesh gateways). type InternalServiceDump interface { Notify(ctx context.Context, req *structs.ServiceDumpRequest, correlationID string, ch chan<- UpdateEvent) error } diff --git a/agent/proxycfg/mesh_gateway.go b/agent/proxycfg/mesh_gateway.go index f80ee537f..93fffdc31 100644 --- a/agent/proxycfg/mesh_gateway.go +++ b/agent/proxycfg/mesh_gateway.go @@ -491,7 +491,7 @@ func (s *handlerMeshGateway) handleUpdate(ctx context.Context, u UpdateEvent, sn } case strings.HasPrefix(u.CorrelationID, "mesh-gateway:"): - resp, ok := u.Result.(*structs.IndexedNodesWithGateways) + resp, ok := u.Result.(*structs.IndexedCheckServiceNodes) if !ok { return fmt.Errorf("invalid type for response: %T", u.Result) } diff --git a/agent/proxycfg/state_test.go b/agent/proxycfg/state_test.go index 825ac84fe..f8cf0834c 100644 --- a/agent/proxycfg/state_test.go +++ b/agent/proxycfg/state_test.go @@ -927,7 +927,7 @@ func TestState_WatchesAndUpdates(t *testing.T) { events: []UpdateEvent{ { CorrelationID: "mesh-gateway:dc4", - Result: &structs.IndexedNodesWithGateways{ + Result: &structs.IndexedCheckServiceNodes{ Nodes: TestGatewayNodesDC4Hostname(t), }, Err: nil, diff --git a/agent/proxycfg/testing.go b/agent/proxycfg/testing.go index d43658947..d5a3d8224 100644 --- a/agent/proxycfg/testing.go +++ b/agent/proxycfg/testing.go @@ -974,7 +974,7 @@ func NewTestDataSources() *TestDataSources { Intentions: NewTestDataSource[*structs.ServiceSpecificRequest, structs.Intentions](), IntentionUpstreams: NewTestDataSource[*structs.ServiceSpecificRequest, *structs.IndexedServiceList](), IntentionUpstreamsDestination: NewTestDataSource[*structs.ServiceSpecificRequest, *structs.IndexedServiceList](), - InternalServiceDump: NewTestDataSource[*structs.ServiceDumpRequest, *structs.IndexedNodesWithGateways](), + InternalServiceDump: NewTestDataSource[*structs.ServiceDumpRequest, *structs.IndexedCheckServiceNodes](), LeafCertificate: NewTestDataSource[*cachetype.ConnectCALeafRequest, *structs.IssuedCert](), PreparedQuery: NewTestDataSource[*structs.PreparedQueryExecuteRequest, *structs.PreparedQueryExecuteResponse](), ResolvedServiceConfig: NewTestDataSource[*structs.ServiceConfigRequest, *structs.ServiceConfigResponse](), @@ -1000,7 +1000,7 @@ type TestDataSources struct { Intentions *TestDataSource[*structs.ServiceSpecificRequest, structs.Intentions] IntentionUpstreams *TestDataSource[*structs.ServiceSpecificRequest, *structs.IndexedServiceList] IntentionUpstreamsDestination *TestDataSource[*structs.ServiceSpecificRequest, *structs.IndexedServiceList] - InternalServiceDump *TestDataSource[*structs.ServiceDumpRequest, *structs.IndexedNodesWithGateways] + InternalServiceDump *TestDataSource[*structs.ServiceDumpRequest, *structs.IndexedCheckServiceNodes] LeafCertificate *TestDataSource[*cachetype.ConnectCALeafRequest, *structs.IssuedCert] PeeredUpstreams *TestDataSource[*structs.PartitionSpecificRequest, *structs.IndexedPeeredServiceList] PreparedQuery *TestDataSource[*structs.PreparedQueryExecuteRequest, *structs.PreparedQueryExecuteResponse] diff --git a/agent/proxycfg/testing_mesh_gateway.go b/agent/proxycfg/testing_mesh_gateway.go index 388ac12b8..f8b6116a2 100644 --- a/agent/proxycfg/testing_mesh_gateway.go +++ b/agent/proxycfg/testing_mesh_gateway.go @@ -316,19 +316,19 @@ func TestConfigSnapshotMeshGateway(t testing.T, variant string, nsFn func(ns *st baseEvents = testSpliceEvents(baseEvents, []UpdateEvent{ { CorrelationID: "mesh-gateway:dc2", - Result: &structs.IndexedNodesWithGateways{ + Result: &structs.IndexedCheckServiceNodes{ Nodes: TestGatewayNodesDC2(t), }, }, { CorrelationID: "mesh-gateway:dc4", - Result: &structs.IndexedNodesWithGateways{ + Result: &structs.IndexedCheckServiceNodes{ Nodes: TestGatewayNodesDC4Hostname(t), }, }, { CorrelationID: "mesh-gateway:dc6", - Result: &structs.IndexedNodesWithGateways{ + Result: &structs.IndexedCheckServiceNodes{ Nodes: TestGatewayNodesDC6Hostname(t), }, }, @@ -376,7 +376,7 @@ func TestConfigSnapshotMeshGateway(t testing.T, variant string, nsFn func(ns *st // Have the cross-dc query mechanism not work for dc2 so // fedstates will infill. CorrelationID: "mesh-gateway:dc2", - Result: &structs.IndexedNodesWithGateways{ + Result: &structs.IndexedCheckServiceNodes{ Nodes: nil, }, }, diff --git a/agent/proxycfg/testing_upstreams.go b/agent/proxycfg/testing_upstreams.go index 5e131af4f..f851ea059 100644 --- a/agent/proxycfg/testing_upstreams.go +++ b/agent/proxycfg/testing_upstreams.go @@ -69,7 +69,7 @@ func setupTestVariationConfigEntriesAndSnapshot( }) events = append(events, UpdateEvent{ CorrelationID: "mesh-gateway:dc2:" + dbUID.String(), - Result: &structs.IndexedNodesWithGateways{ + Result: &structs.IndexedCheckServiceNodes{ Nodes: TestGatewayNodesDC2(t), }, }) @@ -114,13 +114,13 @@ func setupTestVariationConfigEntriesAndSnapshot( }) events = append(events, UpdateEvent{ CorrelationID: "mesh-gateway:dc2:" + dbUID.String(), - Result: &structs.IndexedNodesWithGateways{ + Result: &structs.IndexedCheckServiceNodes{ Nodes: TestGatewayNodesDC2(t), }, }) events = append(events, UpdateEvent{ CorrelationID: "mesh-gateway:dc3:" + dbUID.String(), - Result: &structs.IndexedNodesWithGateways{ + Result: &structs.IndexedCheckServiceNodes{ Nodes: TestGatewayNodesDC3(t), }, }) @@ -141,7 +141,7 @@ func setupTestVariationConfigEntriesAndSnapshot( }) events = append(events, UpdateEvent{ CorrelationID: "mesh-gateway:dc1:" + dbUID.String(), - Result: &structs.IndexedNodesWithGateways{ + Result: &structs.IndexedCheckServiceNodes{ Nodes: TestGatewayNodesDC1(t), }, }) @@ -168,7 +168,7 @@ func setupTestVariationConfigEntriesAndSnapshot( }) events = append(events, UpdateEvent{ CorrelationID: "mesh-gateway:dc1:" + dbUID.String(), - Result: &structs.IndexedNodesWithGateways{ + Result: &structs.IndexedCheckServiceNodes{ Nodes: TestGatewayNodesDC1(t), }, }) diff --git a/agent/proxycfg/upstreams.go b/agent/proxycfg/upstreams.go index e8825e94c..94c872e39 100644 --- a/agent/proxycfg/upstreams.go +++ b/agent/proxycfg/upstreams.go @@ -186,7 +186,7 @@ func (s *handlerUpstreams) handleUpdateUpstreams(ctx context.Context, u UpdateEv } case strings.HasPrefix(u.CorrelationID, "mesh-gateway:"): - resp, ok := u.Result.(*structs.IndexedNodesWithGateways) + resp, ok := u.Result.(*structs.IndexedCheckServiceNodes) if !ok { return fmt.Errorf("invalid type for response: %T", u.Result) } diff --git a/agent/sidecar_service.go b/agent/sidecar_service.go index a41d73d80..72e868e77 100644 --- a/agent/sidecar_service.go +++ b/agent/sidecar_service.go @@ -2,6 +2,7 @@ package agent import ( "fmt" + "strings" "time" "github.com/hashicorp/consul/ipaddr" @@ -13,6 +14,10 @@ func sidecarServiceID(serviceID string) string { return serviceID + "-sidecar-proxy" } +func serviceIDFromSidecarID(sidecarServiceID string) string { + return strings.Split(sidecarServiceID, "-")[0] +} + // sidecarServiceFromNodeService returns a *structs.NodeService representing a // sidecar service with all defaults populated based on the current agent // config. @@ -30,7 +35,7 @@ func sidecarServiceID(serviceID string) string { // registration. This will be the same as the token parameter passed unless the // SidecarService definition contains a distinct one. // TODO: return AddServiceRequest -func (a *Agent) sidecarServiceFromNodeService(ns *structs.NodeService, token string) (*structs.NodeService, []*structs.CheckType, string, error) { +func sidecarServiceFromNodeService(ns *structs.NodeService, token string) (*structs.NodeService, []*structs.CheckType, string, error) { if ns.Connect.SidecarService == nil { return nil, nil, "", nil } @@ -114,41 +119,18 @@ func (a *Agent) sidecarServiceFromNodeService(ns *structs.NodeService, token str } } - if sidecar.Port < 1 { - port, err := a.sidecarPortFromServiceID(sidecar.CompoundServiceID()) - if err != nil { - return nil, nil, "", err - } - sidecar.Port = port - } - // Setup checks checks, err := ns.Connect.SidecarService.CheckTypes() if err != nil { return nil, nil, "", err } - // Setup default check if none given. - if len(checks) < 1 { - // The check should use the sidecar's address because it makes a request to the sidecar. - // If the sidecar's address is empty, we fall back to the address of the local service, as set in - // sidecar.Proxy.LocalServiceAddress, in the hope that the proxy is also accessible on that address - // (which in most cases it is because it's running as a sidecar in the same network). - // We could instead fall back to the address of the service as set by (ns.Address), but I've kept it using - // sidecar.Proxy.LocalServiceAddress so as to not change things too much in the - // process of fixing #14433. - checkAddress := sidecar.Address - if checkAddress == "" { - checkAddress = sidecar.Proxy.LocalServiceAddress - } - checks = sidecarDefaultChecks(ns.ID, checkAddress, sidecar.Port) - } return sidecar, checks, token, nil } -// sidecarPortFromServiceID is used to allocate a unique port for a sidecar proxy. +// sidecarPortFromServiceIDLocked is used to allocate a unique port for a sidecar proxy. // This is called immediately before registration to avoid value collisions. This function assumes the state lock is already held. -func (a *Agent) sidecarPortFromServiceID(sidecarCompoundServiceID structs.ServiceID) (int, error) { +func (a *Agent) sidecarPortFromServiceIDLocked(sidecarCompoundServiceID structs.ServiceID) (int, error) { sidecarPort := 0 // Allocate port if needed (min and max inclusive). @@ -213,11 +195,23 @@ func (a *Agent) sidecarPortFromServiceID(sidecarCompoundServiceID structs.Servic return sidecarPort, nil } -func sidecarDefaultChecks(serviceID string, address string, port int) []*structs.CheckType { +func sidecarDefaultChecks(sidecarID string, sidecarAddress string, proxyServiceAddress string, port int) []*structs.CheckType { + // The check should use the sidecar's address because it makes a request to the sidecar. + // If the sidecar's address is empty, we fall back to the address of the local service, as set in + // sidecar.Proxy.LocalServiceAddress, in the hope that the proxy is also accessible on that address + // (which in most cases it is because it's running as a sidecar in the same network). + // We could instead fall back to the address of the service as set by (ns.Address), but I've kept it using + // sidecar.Proxy.LocalServiceAddress so as to not change things too much in the + // process of fixing #14433. + checkAddress := sidecarAddress + if checkAddress == "" { + checkAddress = proxyServiceAddress + } + serviceID := serviceIDFromSidecarID(sidecarID) return []*structs.CheckType{ { Name: "Connect Sidecar Listening", - TCP: ipaddr.FormatAddressPort(address, port), + TCP: ipaddr.FormatAddressPort(checkAddress, port), Interval: 10 * time.Second, }, { diff --git a/agent/sidecar_service_test.go b/agent/sidecar_service_test.go index 39ab854a6..ac30901ad 100644 --- a/agent/sidecar_service_test.go +++ b/agent/sidecar_service_test.go @@ -54,7 +54,7 @@ func TestAgent_sidecarServiceFromNodeService(t *testing.T) { Kind: structs.ServiceKindConnectProxy, ID: "web1-sidecar-proxy", Service: "web-sidecar-proxy", - Port: 2222, + Port: 0, LocallyRegisteredAsSidecar: true, Proxy: structs.ConnectProxyConfig{ DestinationServiceName: "web", @@ -63,18 +63,8 @@ func TestAgent_sidecarServiceFromNodeService(t *testing.T) { LocalServicePort: 1111, }, }, - wantChecks: []*structs.CheckType{ - { - Name: "Connect Sidecar Listening", - TCP: "127.0.0.1:2222", - Interval: 10 * time.Second, - }, - { - Name: "Connect Sidecar Aliasing web1", - AliasService: "web1", - }, - }, - wantToken: "foo", + wantChecks: nil, + wantToken: "foo", }, { name: "all the allowed overrides", @@ -157,7 +147,7 @@ func TestAgent_sidecarServiceFromNodeService(t *testing.T) { Kind: structs.ServiceKindConnectProxy, ID: "web1-sidecar-proxy", Service: "web-sidecar-proxy", - Port: 2222, + Port: 0, Tags: []string{"foo"}, Meta: map[string]string{"foo": "bar"}, LocallyRegisteredAsSidecar: true, @@ -168,17 +158,7 @@ func TestAgent_sidecarServiceFromNodeService(t *testing.T) { LocalServicePort: 1111, }, }, - wantChecks: []*structs.CheckType{ - { - Name: "Connect Sidecar Listening", - TCP: "127.0.0.1:2222", - Interval: 10 * time.Second, - }, - { - Name: "Connect Sidecar Aliasing web1", - AliasService: "web1", - }, - }, + wantChecks: nil, }, { name: "invalid check type", @@ -215,158 +195,14 @@ func TestAgent_sidecarServiceFromNodeService(t *testing.T) { token: "foo", wantErr: "reserved for internal use", }, - { - name: "uses proxy address for check", - sd: &structs.ServiceDefinition{ - ID: "web1", - Name: "web", - Port: 1111, - Connect: &structs.ServiceConnect{ - SidecarService: &structs.ServiceDefinition{ - Address: "123.123.123.123", - Proxy: &structs.ConnectProxyConfig{ - LocalServiceAddress: "255.255.255.255", - }, - }, - }, - Address: "255.255.255.255", - }, - token: "foo", - wantNS: &structs.NodeService{ - EnterpriseMeta: *structs.DefaultEnterpriseMetaInDefaultPartition(), - Kind: structs.ServiceKindConnectProxy, - ID: "web1-sidecar-proxy", - Service: "web-sidecar-proxy", - Port: 2222, - Address: "123.123.123.123", - LocallyRegisteredAsSidecar: true, - Proxy: structs.ConnectProxyConfig{ - DestinationServiceName: "web", - DestinationServiceID: "web1", - LocalServiceAddress: "255.255.255.255", - LocalServicePort: 1111, - }, - }, - wantChecks: []*structs.CheckType{ - { - Name: "Connect Sidecar Listening", - TCP: "123.123.123.123:2222", - Interval: 10 * time.Second, - }, - { - Name: "Connect Sidecar Aliasing web1", - AliasService: "web1", - }, - }, - wantToken: "foo", - }, - { - name: "uses proxy.local_service_address for check if proxy address is empty", - sd: &structs.ServiceDefinition{ - ID: "web1", - Name: "web", - Port: 1111, - Connect: &structs.ServiceConnect{ - SidecarService: &structs.ServiceDefinition{ - Address: "", // Proxy address empty. - Proxy: &structs.ConnectProxyConfig{ - LocalServiceAddress: "1.2.3.4", - }, - }, - }, - Address: "", // Service address empty. - }, - token: "foo", - wantNS: &structs.NodeService{ - EnterpriseMeta: *structs.DefaultEnterpriseMetaInDefaultPartition(), - Kind: structs.ServiceKindConnectProxy, - ID: "web1-sidecar-proxy", - Service: "web-sidecar-proxy", - Port: 2222, - Address: "", - LocallyRegisteredAsSidecar: true, - Proxy: structs.ConnectProxyConfig{ - DestinationServiceName: "web", - DestinationServiceID: "web1", - LocalServiceAddress: "1.2.3.4", - LocalServicePort: 1111, - }, - }, - wantChecks: []*structs.CheckType{ - { - Name: "Connect Sidecar Listening", - TCP: "1.2.3.4:2222", - Interval: 10 * time.Second, - }, - { - Name: "Connect Sidecar Aliasing web1", - AliasService: "web1", - }, - }, - wantToken: "foo", - }, - { - name: "uses 127.0.0.1 for check if proxy and proxy.local_service_address are empty", - sd: &structs.ServiceDefinition{ - ID: "web1", - Name: "web", - Port: 1111, - Connect: &structs.ServiceConnect{ - SidecarService: &structs.ServiceDefinition{ - Address: "", - Proxy: &structs.ConnectProxyConfig{ - LocalServiceAddress: "", - }, - }, - }, - Address: "", - }, - token: "foo", - wantNS: &structs.NodeService{ - EnterpriseMeta: *structs.DefaultEnterpriseMetaInDefaultPartition(), - Kind: structs.ServiceKindConnectProxy, - ID: "web1-sidecar-proxy", - Service: "web-sidecar-proxy", - Port: 2222, - Address: "", - LocallyRegisteredAsSidecar: true, - Proxy: structs.ConnectProxyConfig{ - DestinationServiceName: "web", - DestinationServiceID: "web1", - LocalServiceAddress: "127.0.0.1", - LocalServicePort: 1111, - }, - }, - wantChecks: []*structs.CheckType{ - { - Name: "Connect Sidecar Listening", - TCP: "127.0.0.1:2222", - Interval: 10 * time.Second, - }, - { - Name: "Connect Sidecar Aliasing web1", - AliasService: "web1", - }, - }, - wantToken: "foo", - }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - hcl := ` - ports { - sidecar_min_port = 2222 - sidecar_max_port = 2222 - } - ` - a := StartTestAgent(t, TestAgent{Name: "jones", HCL: hcl}) - defer a.Shutdown() - ns := tt.sd.NodeService() err := ns.Validate() require.NoError(t, err, "Invalid test case - NodeService must validate") - gotNS, gotChecks, gotToken, err := a.sidecarServiceFromNodeService(ns, tt.token) + gotNS, gotChecks, gotToken, err := sidecarServiceFromNodeService(ns, tt.token) if tt.wantErr != "" { require.Error(t, err) require.Contains(t, err.Error(), tt.wantErr) @@ -464,7 +300,7 @@ func TestAgent_SidecarPortFromServiceID(t *testing.T) { } ` } - a := StartTestAgent(t, TestAgent{Name: "jones", HCL: hcl}) + a := NewTestAgent(t, hcl) defer a.Shutdown() if tt.preRegister != nil { @@ -472,7 +308,7 @@ func TestAgent_SidecarPortFromServiceID(t *testing.T) { require.NoError(t, err) } - gotPort, err := a.sidecarPortFromServiceID(structs.ServiceID{ID: tt.serviceID, EnterpriseMeta: tt.enterpriseMeta}) + gotPort, err := a.sidecarPortFromServiceIDLocked(structs.ServiceID{ID: tt.serviceID, EnterpriseMeta: tt.enterpriseMeta}) if tt.wantErr != "" { require.Error(t, err) @@ -485,3 +321,52 @@ func TestAgent_SidecarPortFromServiceID(t *testing.T) { }) } } + +func TestAgent_SidecarDefaultChecks(t *testing.T) { + tests := []struct { + name string + svcAddress string + proxyLocalSvcAddress string + port int + wantChecks []*structs.CheckType + }{{ + name: "uses proxy address for check", + svcAddress: "123.123.123.123", + proxyLocalSvcAddress: "255.255.255.255", + port: 2222, + wantChecks: []*structs.CheckType{ + { + Name: "Connect Sidecar Listening", + TCP: "123.123.123.123:2222", + Interval: 10 * time.Second, + }, + { + Name: "Connect Sidecar Aliasing web1", + AliasService: "web1", + }, + }, + }, + { + name: "uses proxy.local_service_address for check if proxy address is empty", + proxyLocalSvcAddress: "1.2.3.4", + port: 2222, + wantChecks: []*structs.CheckType{ + { + Name: "Connect Sidecar Listening", + TCP: "1.2.3.4:2222", + Interval: 10 * time.Second, + }, + { + Name: "Connect Sidecar Aliasing web1", + AliasService: "web1", + }, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + gotChecks := sidecarDefaultChecks("web1", tt.svcAddress, tt.proxyLocalSvcAddress, tt.port) + require.Equal(t, tt.wantChecks, gotChecks) + }) + } +} diff --git a/agent/structs/connect_ca.go b/agent/structs/connect_ca.go index dfb0c9ab4..bc50b416e 100644 --- a/agent/structs/connect_ca.go +++ b/agent/structs/connect_ca.go @@ -224,6 +224,10 @@ type IssuedCert struct { // AgentURI is the cert URI value. AgentURI string `json:",omitempty"` + // ServerURI is the URI value of a cert issued for a server agent. + // The same URI is shared by all servers in a Consul datacenter. + ServerURI string `json:",omitempty"` + // Kind is the kind of service for which the cert was issued. Kind ServiceKind `json:",omitempty"` // KindURI is the cert URI value. diff --git a/agent/structs/structs.go b/agent/structs/structs.go index 830168888..b3b832567 100644 --- a/agent/structs/structs.go +++ b/agent/structs/structs.go @@ -1257,8 +1257,9 @@ type NodeService struct { // a pointer so that we never have to nil-check this. Connect ServiceConnect + // TODO: rename to reflect that this is used to express future intent to register. // LocallyRegisteredAsSidecar is private as it is only used by a local agent - // state to track if the service was registered from a nested sidecar_service + // state to track if the service was or will be registered from a nested sidecar_service // block. We need to track that so we can know whether we need to deregister // it automatically too if it's removed from the service definition or if the // parent service is deregistered. Relying only on ID would cause us to diff --git a/agent/ui_endpoint.go b/agent/ui_endpoint.go index df6f359de..a418a4017 100644 --- a/agent/ui_endpoint.go +++ b/agent/ui_endpoint.go @@ -771,6 +771,7 @@ func (s *HTTPHandlers) UIMetricsProxy(resp http.ResponseWriter, req *http.Reques Director: func(r *http.Request) { r.URL = u }, + Transport: s.proxyTransport, ErrorLog: log.StandardLogger(&hclog.StandardLoggerOptions{ InferLevels: true, }), diff --git a/api/txn.go b/api/txn.go index 59fd1c0d9..4aa06d9f5 100644 --- a/api/txn.go +++ b/api/txn.go @@ -67,6 +67,7 @@ const ( KVLock KVOp = "lock" KVUnlock KVOp = "unlock" KVGet KVOp = "get" + KVGetOrEmpty KVOp = "get-or-empty" KVGetTree KVOp = "get-tree" KVCheckSession KVOp = "check-session" KVCheckIndex KVOp = "check-index" diff --git a/go.mod b/go.mod index 1ade7d6de..3c518b17b 100644 --- a/go.mod +++ b/go.mod @@ -6,6 +6,8 @@ replace github.com/hashicorp/consul/api => ./api replace github.com/hashicorp/consul/sdk => ./sdk +replace github.com/hashicorp/consul/proto-public => ./proto-public + replace launchpad.net/gocheck => github.com/go-check/check v0.0.0-20140225173054-eb6ee6f84d0a require ( @@ -28,6 +30,7 @@ require ( github.com/hashicorp/consul-awsauth v0.0.0-20220713182709-05ac1c5c2706 github.com/hashicorp/consul-net-rpc v0.0.0-20220307172752-3602954411b4 github.com/hashicorp/consul/api v1.13.1 + github.com/hashicorp/consul/proto-public v0.1.0 github.com/hashicorp/consul/sdk v0.10.0 github.com/hashicorp/go-bexpr v0.1.2 github.com/hashicorp/go-checkpoint v0.5.0 diff --git a/proto-public/go.mod b/proto-public/go.mod new file mode 100644 index 000000000..9870dccfb --- /dev/null +++ b/proto-public/go.mod @@ -0,0 +1,16 @@ +module github.com/hashicorp/consul/proto-public + +go 1.19 + +require ( + github.com/golang/protobuf v1.5.0 + google.golang.org/grpc v1.37.1 + google.golang.org/protobuf v1.27.1 +) + +require ( + golang.org/x/net v0.0.0-20190311183353-d8887717615a // indirect + golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a // indirect + golang.org/x/text v0.3.0 // indirect + google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013 // indirect +) diff --git a/proto-public/go.sum b/proto-public/go.sum new file mode 100644 index 000000000..59e6e6727 --- /dev/null +++ b/proto-public/go.sum @@ -0,0 +1,88 @@ +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.5.0 h1:LUVKkCeviFUMKqHa4tXIIij/lbhnMbP7Fn5wKdKkRh4= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a h1:oWX7TPOiFAMXLq8o0ikBYfCJVlRHBcsciT5bXOrH628= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a h1:1BGLXjeY4akVXGgbC9HugT3Jv3hCI0z56oJR5vAMgBU= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013 h1:+kGHl1aib/qcwaRi1CbqBZ1rk19r85MNUf8HaBghugY= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.37.1 h1:ARnQJNWxGyYJpdf/JXscNlQr/uv607ZPU9Z7ogHi+iI= +google.golang.org/grpc v1.37.1/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ= +google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/proto/pbconnect/connect.gen.go b/proto/pbconnect/connect.gen.go index 701400d80..abecd9b4c 100644 --- a/proto/pbconnect/connect.gen.go +++ b/proto/pbconnect/connect.gen.go @@ -93,6 +93,7 @@ func IssuedCertToStructsIssuedCert(s *IssuedCert, t *structs.IssuedCert) { t.ServiceURI = s.ServiceURI t.Agent = s.Agent t.AgentURI = s.AgentURI + t.ServerURI = s.ServerURI t.Kind = structs.ServiceKind(s.Kind) t.KindURI = s.KindURI t.ValidAfter = structs.TimeFromProto(s.ValidAfter) @@ -111,6 +112,7 @@ func IssuedCertFromStructsIssuedCert(t *structs.IssuedCert, s *IssuedCert) { s.ServiceURI = t.ServiceURI s.Agent = t.Agent s.AgentURI = t.AgentURI + s.ServerURI = t.ServerURI s.Kind = string(t.Kind) s.KindURI = t.KindURI s.ValidAfter = structs.TimeToProto(t.ValidAfter) diff --git a/proto/pbconnect/connect.pb.go b/proto/pbconnect/connect.pb.go index 5e6adf740..89100474f 100644 --- a/proto/pbconnect/connect.pb.go +++ b/proto/pbconnect/connect.pb.go @@ -377,6 +377,9 @@ type IssuedCert struct { Kind string `protobuf:"bytes,12,opt,name=Kind,proto3" json:"Kind,omitempty"` // KindURI is the cert URI value. KindURI string `protobuf:"bytes,13,opt,name=KindURI,proto3" json:"KindURI,omitempty"` + // ServerURI is the URI value of a cert issued for a server agent. + // The same URI is shared by all servers in a Consul datacenter. + ServerURI string `protobuf:"bytes,14,opt,name=ServerURI,proto3" json:"ServerURI,omitempty"` // ValidAfter and ValidBefore are the validity periods for the // certificate. // mog: func-to=structs.TimeFromProto func-from=structs.TimeToProto @@ -485,6 +488,13 @@ func (x *IssuedCert) GetKindURI() string { return "" } +func (x *IssuedCert) GetServerURI() string { + if x != nil { + return x.ServerURI + } + return "" +} + func (x *IssuedCert) GetValidAfter() *timestamppb.Timestamp { if x != nil { return x.ValidAfter @@ -579,7 +589,7 @@ var file_proto_pbconnect_connect_proto_rawDesc = []byte{ 0x32, 0x2b, 0x2e, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x52, 0x61, 0x66, 0x74, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x52, 0x09, 0x52, - 0x61, 0x66, 0x74, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x22, 0xa9, 0x04, 0x0a, 0x0a, 0x49, 0x73, 0x73, + 0x61, 0x66, 0x74, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x22, 0xc7, 0x04, 0x0a, 0x0a, 0x49, 0x73, 0x73, 0x75, 0x65, 0x64, 0x43, 0x65, 0x72, 0x74, 0x12, 0x22, 0x0a, 0x0c, 0x53, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x53, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x12, 0x18, 0x0a, 0x07, 0x43, @@ -596,42 +606,44 @@ var file_proto_pbconnect_connect_proto_rawDesc = []byte{ 0x67, 0x65, 0x6e, 0x74, 0x55, 0x52, 0x49, 0x12, 0x12, 0x0a, 0x04, 0x4b, 0x69, 0x6e, 0x64, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x4b, 0x69, 0x6e, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x4b, 0x69, 0x6e, 0x64, 0x55, 0x52, 0x49, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x4b, 0x69, - 0x6e, 0x64, 0x55, 0x52, 0x49, 0x12, 0x3a, 0x0a, 0x0a, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x41, 0x66, - 0x74, 0x65, 0x72, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, - 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, - 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0a, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x41, 0x66, 0x74, 0x65, - 0x72, 0x12, 0x3c, 0x0a, 0x0b, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x42, 0x65, 0x66, 0x6f, 0x72, 0x65, - 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, - 0x6d, 0x70, 0x52, 0x0b, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x42, 0x65, 0x66, 0x6f, 0x72, 0x65, 0x12, - 0x58, 0x0a, 0x0e, 0x45, 0x6e, 0x74, 0x65, 0x72, 0x70, 0x72, 0x69, 0x73, 0x65, 0x4d, 0x65, 0x74, - 0x61, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x30, 0x2e, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, - 0x6f, 0x72, 0x70, 0x2e, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, - 0x6e, 0x61, 0x6c, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x45, 0x6e, 0x74, 0x65, 0x72, - 0x70, 0x72, 0x69, 0x73, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x52, 0x0e, 0x45, 0x6e, 0x74, 0x65, 0x72, - 0x70, 0x72, 0x69, 0x73, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x12, 0x49, 0x0a, 0x09, 0x52, 0x61, 0x66, - 0x74, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x68, - 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2e, - 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, - 0x52, 0x61, 0x66, 0x74, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x52, 0x09, 0x52, 0x61, 0x66, 0x74, 0x49, - 0x6e, 0x64, 0x65, 0x78, 0x42, 0x8a, 0x02, 0x0a, 0x25, 0x63, 0x6f, 0x6d, 0x2e, 0x68, 0x61, 0x73, + 0x6e, 0x64, 0x55, 0x52, 0x49, 0x12, 0x1c, 0x0a, 0x09, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x55, + 0x52, 0x49, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, + 0x55, 0x52, 0x49, 0x12, 0x3a, 0x0a, 0x0a, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x41, 0x66, 0x74, 0x65, + 0x72, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, + 0x61, 0x6d, 0x70, 0x52, 0x0a, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x41, 0x66, 0x74, 0x65, 0x72, 0x12, + 0x3c, 0x0a, 0x0b, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x42, 0x65, 0x66, 0x6f, 0x72, 0x65, 0x18, 0x09, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, + 0x52, 0x0b, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x42, 0x65, 0x66, 0x6f, 0x72, 0x65, 0x12, 0x58, 0x0a, + 0x0e, 0x45, 0x6e, 0x74, 0x65, 0x72, 0x70, 0x72, 0x69, 0x73, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x18, + 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x30, 0x2e, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, + 0x70, 0x2e, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, + 0x6c, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x45, 0x6e, 0x74, 0x65, 0x72, 0x70, 0x72, + 0x69, 0x73, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x52, 0x0e, 0x45, 0x6e, 0x74, 0x65, 0x72, 0x70, 0x72, + 0x69, 0x73, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x12, 0x49, 0x0a, 0x09, 0x52, 0x61, 0x66, 0x74, 0x49, + 0x6e, 0x64, 0x65, 0x78, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2e, 0x69, 0x6e, - 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2e, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x42, 0x0c, - 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x2b, - 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73, 0x68, 0x69, - 0x63, 0x6f, 0x72, 0x70, 0x2f, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2f, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x2f, 0x70, 0x62, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0xa2, 0x02, 0x04, 0x48, 0x43, - 0x49, 0x43, 0xaa, 0x02, 0x21, 0x48, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x43, - 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2e, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2e, 0x43, - 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0xca, 0x02, 0x21, 0x48, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, - 0x72, 0x70, 0x5c, 0x43, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x5c, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, - 0x61, 0x6c, 0x5c, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0xe2, 0x02, 0x2d, 0x48, 0x61, 0x73, - 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x5c, 0x43, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x5c, 0x49, 0x6e, - 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x5c, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x5c, 0x47, - 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x24, 0x48, 0x61, 0x73, - 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x3a, 0x3a, 0x43, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x3a, 0x3a, - 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x3a, 0x3a, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, - 0x74, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x52, 0x61, + 0x66, 0x74, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x52, 0x09, 0x52, 0x61, 0x66, 0x74, 0x49, 0x6e, 0x64, + 0x65, 0x78, 0x42, 0x8a, 0x02, 0x0a, 0x25, 0x63, 0x6f, 0x6d, 0x2e, 0x68, 0x61, 0x73, 0x68, 0x69, + 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2e, 0x69, 0x6e, 0x74, 0x65, + 0x72, 0x6e, 0x61, 0x6c, 0x2e, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x42, 0x0c, 0x43, 0x6f, + 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x2b, 0x67, 0x69, + 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, + 0x72, 0x70, 0x2f, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, + 0x70, 0x62, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0xa2, 0x02, 0x04, 0x48, 0x43, 0x49, 0x43, + 0xaa, 0x02, 0x21, 0x48, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x43, 0x6f, 0x6e, + 0x73, 0x75, 0x6c, 0x2e, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2e, 0x43, 0x6f, 0x6e, + 0x6e, 0x65, 0x63, 0x74, 0xca, 0x02, 0x21, 0x48, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, + 0x5c, 0x43, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x5c, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, + 0x5c, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0xe2, 0x02, 0x2d, 0x48, 0x61, 0x73, 0x68, 0x69, + 0x63, 0x6f, 0x72, 0x70, 0x5c, 0x43, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x5c, 0x49, 0x6e, 0x74, 0x65, + 0x72, 0x6e, 0x61, 0x6c, 0x5c, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x5c, 0x47, 0x50, 0x42, + 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x24, 0x48, 0x61, 0x73, 0x68, 0x69, + 0x63, 0x6f, 0x72, 0x70, 0x3a, 0x3a, 0x43, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x3a, 0x3a, 0x49, 0x6e, + 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x3a, 0x3a, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x62, + 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/proto/pbconnect/connect.proto b/proto/pbconnect/connect.proto index 640987316..071f76dea 100644 --- a/proto/pbconnect/connect.proto +++ b/proto/pbconnect/connect.proto @@ -165,6 +165,10 @@ message IssuedCert { // KindURI is the cert URI value. string KindURI = 13; + // ServerURI is the URI value of a cert issued for a server agent. + // The same URI is shared by all servers in a Consul datacenter. + string ServerURI = 14; + // ValidAfter and ValidBefore are the validity periods for the // certificate. // mog: func-to=structs.TimeFromProto func-from=structs.TimeToProto diff --git a/proto/pbpeering/peering.pb.binary.go b/proto/pbpeering/peering.pb.binary.go index 2e9d5c71c..499e31226 100644 --- a/proto/pbpeering/peering.pb.binary.go +++ b/proto/pbpeering/peering.pb.binary.go @@ -107,6 +107,16 @@ func (msg *PeeringTrustBundle) UnmarshalBinary(b []byte) error { return proto.Unmarshal(b, msg) } +// MarshalBinary implements encoding.BinaryMarshaler +func (msg *PeeringServerAddresses) MarshalBinary() ([]byte, error) { + return proto.Marshal(msg) +} + +// UnmarshalBinary implements encoding.BinaryUnmarshaler +func (msg *PeeringServerAddresses) UnmarshalBinary(b []byte) error { + return proto.Unmarshal(b, msg) +} + // MarshalBinary implements encoding.BinaryMarshaler func (msg *PeeringReadRequest) MarshalBinary() ([]byte, error) { return proto.Marshal(msg) diff --git a/proto/pbpeering/peering.pb.go b/proto/pbpeering/peering.pb.go index abd0ea186..8fdff0246 100644 --- a/proto/pbpeering/peering.pb.go +++ b/proto/pbpeering/peering.pb.go @@ -568,6 +568,55 @@ func (x *PeeringTrustBundle) GetModifyIndex() uint64 { return 0 } +// PeeringServerAddresses contains the latest snapshot of all known +// server addresses for a peer. +type PeeringServerAddresses struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Addresses []string `protobuf:"bytes,1,rep,name=Addresses,proto3" json:"Addresses,omitempty"` +} + +func (x *PeeringServerAddresses) Reset() { + *x = PeeringServerAddresses{} + if protoimpl.UnsafeEnabled { + mi := &file_proto_pbpeering_peering_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *PeeringServerAddresses) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*PeeringServerAddresses) ProtoMessage() {} + +func (x *PeeringServerAddresses) ProtoReflect() protoreflect.Message { + mi := &file_proto_pbpeering_peering_proto_msgTypes[4] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use PeeringServerAddresses.ProtoReflect.Descriptor instead. +func (*PeeringServerAddresses) Descriptor() ([]byte, []int) { + return file_proto_pbpeering_peering_proto_rawDescGZIP(), []int{4} +} + +func (x *PeeringServerAddresses) GetAddresses() []string { + if x != nil { + return x.Addresses + } + return nil +} + // @consul-rpc-glue: LeaderReadTODO type PeeringReadRequest struct { state protoimpl.MessageState @@ -581,7 +630,7 @@ type PeeringReadRequest struct { func (x *PeeringReadRequest) Reset() { *x = PeeringReadRequest{} if protoimpl.UnsafeEnabled { - mi := &file_proto_pbpeering_peering_proto_msgTypes[4] + mi := &file_proto_pbpeering_peering_proto_msgTypes[5] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -594,7 +643,7 @@ func (x *PeeringReadRequest) String() string { func (*PeeringReadRequest) ProtoMessage() {} func (x *PeeringReadRequest) ProtoReflect() protoreflect.Message { - mi := &file_proto_pbpeering_peering_proto_msgTypes[4] + mi := &file_proto_pbpeering_peering_proto_msgTypes[5] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -607,7 +656,7 @@ func (x *PeeringReadRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use PeeringReadRequest.ProtoReflect.Descriptor instead. func (*PeeringReadRequest) Descriptor() ([]byte, []int) { - return file_proto_pbpeering_peering_proto_rawDescGZIP(), []int{4} + return file_proto_pbpeering_peering_proto_rawDescGZIP(), []int{5} } func (x *PeeringReadRequest) GetName() string { @@ -635,7 +684,7 @@ type PeeringReadResponse struct { func (x *PeeringReadResponse) Reset() { *x = PeeringReadResponse{} if protoimpl.UnsafeEnabled { - mi := &file_proto_pbpeering_peering_proto_msgTypes[5] + mi := &file_proto_pbpeering_peering_proto_msgTypes[6] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -648,7 +697,7 @@ func (x *PeeringReadResponse) String() string { func (*PeeringReadResponse) ProtoMessage() {} func (x *PeeringReadResponse) ProtoReflect() protoreflect.Message { - mi := &file_proto_pbpeering_peering_proto_msgTypes[5] + mi := &file_proto_pbpeering_peering_proto_msgTypes[6] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -661,7 +710,7 @@ func (x *PeeringReadResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use PeeringReadResponse.ProtoReflect.Descriptor instead. func (*PeeringReadResponse) Descriptor() ([]byte, []int) { - return file_proto_pbpeering_peering_proto_rawDescGZIP(), []int{5} + return file_proto_pbpeering_peering_proto_rawDescGZIP(), []int{6} } func (x *PeeringReadResponse) GetPeering() *Peering { @@ -683,7 +732,7 @@ type PeeringListRequest struct { func (x *PeeringListRequest) Reset() { *x = PeeringListRequest{} if protoimpl.UnsafeEnabled { - mi := &file_proto_pbpeering_peering_proto_msgTypes[6] + mi := &file_proto_pbpeering_peering_proto_msgTypes[7] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -696,7 +745,7 @@ func (x *PeeringListRequest) String() string { func (*PeeringListRequest) ProtoMessage() {} func (x *PeeringListRequest) ProtoReflect() protoreflect.Message { - mi := &file_proto_pbpeering_peering_proto_msgTypes[6] + mi := &file_proto_pbpeering_peering_proto_msgTypes[7] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -709,7 +758,7 @@ func (x *PeeringListRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use PeeringListRequest.ProtoReflect.Descriptor instead. func (*PeeringListRequest) Descriptor() ([]byte, []int) { - return file_proto_pbpeering_peering_proto_rawDescGZIP(), []int{6} + return file_proto_pbpeering_peering_proto_rawDescGZIP(), []int{7} } func (x *PeeringListRequest) GetPartition() string { @@ -730,7 +779,7 @@ type PeeringListResponse struct { func (x *PeeringListResponse) Reset() { *x = PeeringListResponse{} if protoimpl.UnsafeEnabled { - mi := &file_proto_pbpeering_peering_proto_msgTypes[7] + mi := &file_proto_pbpeering_peering_proto_msgTypes[8] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -743,7 +792,7 @@ func (x *PeeringListResponse) String() string { func (*PeeringListResponse) ProtoMessage() {} func (x *PeeringListResponse) ProtoReflect() protoreflect.Message { - mi := &file_proto_pbpeering_peering_proto_msgTypes[7] + mi := &file_proto_pbpeering_peering_proto_msgTypes[8] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -756,7 +805,7 @@ func (x *PeeringListResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use PeeringListResponse.ProtoReflect.Descriptor instead. func (*PeeringListResponse) Descriptor() ([]byte, []int) { - return file_proto_pbpeering_peering_proto_rawDescGZIP(), []int{7} + return file_proto_pbpeering_peering_proto_rawDescGZIP(), []int{8} } func (x *PeeringListResponse) GetPeerings() []*Peering { @@ -783,7 +832,7 @@ type PeeringWriteRequest struct { func (x *PeeringWriteRequest) Reset() { *x = PeeringWriteRequest{} if protoimpl.UnsafeEnabled { - mi := &file_proto_pbpeering_peering_proto_msgTypes[8] + mi := &file_proto_pbpeering_peering_proto_msgTypes[9] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -796,7 +845,7 @@ func (x *PeeringWriteRequest) String() string { func (*PeeringWriteRequest) ProtoMessage() {} func (x *PeeringWriteRequest) ProtoReflect() protoreflect.Message { - mi := &file_proto_pbpeering_peering_proto_msgTypes[8] + mi := &file_proto_pbpeering_peering_proto_msgTypes[9] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -809,7 +858,7 @@ func (x *PeeringWriteRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use PeeringWriteRequest.ProtoReflect.Descriptor instead. func (*PeeringWriteRequest) Descriptor() ([]byte, []int) { - return file_proto_pbpeering_peering_proto_rawDescGZIP(), []int{8} + return file_proto_pbpeering_peering_proto_rawDescGZIP(), []int{9} } func (x *PeeringWriteRequest) GetPeering() *Peering { @@ -843,7 +892,7 @@ type PeeringWriteResponse struct { func (x *PeeringWriteResponse) Reset() { *x = PeeringWriteResponse{} if protoimpl.UnsafeEnabled { - mi := &file_proto_pbpeering_peering_proto_msgTypes[9] + mi := &file_proto_pbpeering_peering_proto_msgTypes[10] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -856,7 +905,7 @@ func (x *PeeringWriteResponse) String() string { func (*PeeringWriteResponse) ProtoMessage() {} func (x *PeeringWriteResponse) ProtoReflect() protoreflect.Message { - mi := &file_proto_pbpeering_peering_proto_msgTypes[9] + mi := &file_proto_pbpeering_peering_proto_msgTypes[10] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -869,7 +918,7 @@ func (x *PeeringWriteResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use PeeringWriteResponse.ProtoReflect.Descriptor instead. func (*PeeringWriteResponse) Descriptor() ([]byte, []int) { - return file_proto_pbpeering_peering_proto_rawDescGZIP(), []int{9} + return file_proto_pbpeering_peering_proto_rawDescGZIP(), []int{10} } type PeeringDeleteRequest struct { @@ -884,7 +933,7 @@ type PeeringDeleteRequest struct { func (x *PeeringDeleteRequest) Reset() { *x = PeeringDeleteRequest{} if protoimpl.UnsafeEnabled { - mi := &file_proto_pbpeering_peering_proto_msgTypes[10] + mi := &file_proto_pbpeering_peering_proto_msgTypes[11] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -897,7 +946,7 @@ func (x *PeeringDeleteRequest) String() string { func (*PeeringDeleteRequest) ProtoMessage() {} func (x *PeeringDeleteRequest) ProtoReflect() protoreflect.Message { - mi := &file_proto_pbpeering_peering_proto_msgTypes[10] + mi := &file_proto_pbpeering_peering_proto_msgTypes[11] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -910,7 +959,7 @@ func (x *PeeringDeleteRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use PeeringDeleteRequest.ProtoReflect.Descriptor instead. func (*PeeringDeleteRequest) Descriptor() ([]byte, []int) { - return file_proto_pbpeering_peering_proto_rawDescGZIP(), []int{10} + return file_proto_pbpeering_peering_proto_rawDescGZIP(), []int{11} } func (x *PeeringDeleteRequest) GetName() string { @@ -936,7 +985,7 @@ type PeeringDeleteResponse struct { func (x *PeeringDeleteResponse) Reset() { *x = PeeringDeleteResponse{} if protoimpl.UnsafeEnabled { - mi := &file_proto_pbpeering_peering_proto_msgTypes[11] + mi := &file_proto_pbpeering_peering_proto_msgTypes[12] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -949,7 +998,7 @@ func (x *PeeringDeleteResponse) String() string { func (*PeeringDeleteResponse) ProtoMessage() {} func (x *PeeringDeleteResponse) ProtoReflect() protoreflect.Message { - mi := &file_proto_pbpeering_peering_proto_msgTypes[11] + mi := &file_proto_pbpeering_peering_proto_msgTypes[12] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -962,7 +1011,7 @@ func (x *PeeringDeleteResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use PeeringDeleteResponse.ProtoReflect.Descriptor instead. func (*PeeringDeleteResponse) Descriptor() ([]byte, []int) { - return file_proto_pbpeering_peering_proto_rawDescGZIP(), []int{11} + return file_proto_pbpeering_peering_proto_rawDescGZIP(), []int{12} } type TrustBundleListByServiceRequest struct { @@ -979,7 +1028,7 @@ type TrustBundleListByServiceRequest struct { func (x *TrustBundleListByServiceRequest) Reset() { *x = TrustBundleListByServiceRequest{} if protoimpl.UnsafeEnabled { - mi := &file_proto_pbpeering_peering_proto_msgTypes[12] + mi := &file_proto_pbpeering_peering_proto_msgTypes[13] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -992,7 +1041,7 @@ func (x *TrustBundleListByServiceRequest) String() string { func (*TrustBundleListByServiceRequest) ProtoMessage() {} func (x *TrustBundleListByServiceRequest) ProtoReflect() protoreflect.Message { - mi := &file_proto_pbpeering_peering_proto_msgTypes[12] + mi := &file_proto_pbpeering_peering_proto_msgTypes[13] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1005,7 +1054,7 @@ func (x *TrustBundleListByServiceRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use TrustBundleListByServiceRequest.ProtoReflect.Descriptor instead. func (*TrustBundleListByServiceRequest) Descriptor() ([]byte, []int) { - return file_proto_pbpeering_peering_proto_rawDescGZIP(), []int{12} + return file_proto_pbpeering_peering_proto_rawDescGZIP(), []int{13} } func (x *TrustBundleListByServiceRequest) GetServiceName() string { @@ -1048,7 +1097,7 @@ type TrustBundleListByServiceResponse struct { func (x *TrustBundleListByServiceResponse) Reset() { *x = TrustBundleListByServiceResponse{} if protoimpl.UnsafeEnabled { - mi := &file_proto_pbpeering_peering_proto_msgTypes[13] + mi := &file_proto_pbpeering_peering_proto_msgTypes[14] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1061,7 +1110,7 @@ func (x *TrustBundleListByServiceResponse) String() string { func (*TrustBundleListByServiceResponse) ProtoMessage() {} func (x *TrustBundleListByServiceResponse) ProtoReflect() protoreflect.Message { - mi := &file_proto_pbpeering_peering_proto_msgTypes[13] + mi := &file_proto_pbpeering_peering_proto_msgTypes[14] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1074,7 +1123,7 @@ func (x *TrustBundleListByServiceResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use TrustBundleListByServiceResponse.ProtoReflect.Descriptor instead. func (*TrustBundleListByServiceResponse) Descriptor() ([]byte, []int) { - return file_proto_pbpeering_peering_proto_rawDescGZIP(), []int{13} + return file_proto_pbpeering_peering_proto_rawDescGZIP(), []int{14} } func (x *TrustBundleListByServiceResponse) GetIndex() uint64 { @@ -1103,7 +1152,7 @@ type TrustBundleReadRequest struct { func (x *TrustBundleReadRequest) Reset() { *x = TrustBundleReadRequest{} if protoimpl.UnsafeEnabled { - mi := &file_proto_pbpeering_peering_proto_msgTypes[14] + mi := &file_proto_pbpeering_peering_proto_msgTypes[15] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1116,7 +1165,7 @@ func (x *TrustBundleReadRequest) String() string { func (*TrustBundleReadRequest) ProtoMessage() {} func (x *TrustBundleReadRequest) ProtoReflect() protoreflect.Message { - mi := &file_proto_pbpeering_peering_proto_msgTypes[14] + mi := &file_proto_pbpeering_peering_proto_msgTypes[15] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1129,7 +1178,7 @@ func (x *TrustBundleReadRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use TrustBundleReadRequest.ProtoReflect.Descriptor instead. func (*TrustBundleReadRequest) Descriptor() ([]byte, []int) { - return file_proto_pbpeering_peering_proto_rawDescGZIP(), []int{14} + return file_proto_pbpeering_peering_proto_rawDescGZIP(), []int{15} } func (x *TrustBundleReadRequest) GetName() string { @@ -1158,7 +1207,7 @@ type TrustBundleReadResponse struct { func (x *TrustBundleReadResponse) Reset() { *x = TrustBundleReadResponse{} if protoimpl.UnsafeEnabled { - mi := &file_proto_pbpeering_peering_proto_msgTypes[15] + mi := &file_proto_pbpeering_peering_proto_msgTypes[16] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1171,7 +1220,7 @@ func (x *TrustBundleReadResponse) String() string { func (*TrustBundleReadResponse) ProtoMessage() {} func (x *TrustBundleReadResponse) ProtoReflect() protoreflect.Message { - mi := &file_proto_pbpeering_peering_proto_msgTypes[15] + mi := &file_proto_pbpeering_peering_proto_msgTypes[16] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1184,7 +1233,7 @@ func (x *TrustBundleReadResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use TrustBundleReadResponse.ProtoReflect.Descriptor instead. func (*TrustBundleReadResponse) Descriptor() ([]byte, []int) { - return file_proto_pbpeering_peering_proto_rawDescGZIP(), []int{15} + return file_proto_pbpeering_peering_proto_rawDescGZIP(), []int{16} } func (x *TrustBundleReadResponse) GetIndex() uint64 { @@ -1213,7 +1262,7 @@ type PeeringTerminateByIDRequest struct { func (x *PeeringTerminateByIDRequest) Reset() { *x = PeeringTerminateByIDRequest{} if protoimpl.UnsafeEnabled { - mi := &file_proto_pbpeering_peering_proto_msgTypes[16] + mi := &file_proto_pbpeering_peering_proto_msgTypes[17] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1226,7 +1275,7 @@ func (x *PeeringTerminateByIDRequest) String() string { func (*PeeringTerminateByIDRequest) ProtoMessage() {} func (x *PeeringTerminateByIDRequest) ProtoReflect() protoreflect.Message { - mi := &file_proto_pbpeering_peering_proto_msgTypes[16] + mi := &file_proto_pbpeering_peering_proto_msgTypes[17] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1239,7 +1288,7 @@ func (x *PeeringTerminateByIDRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use PeeringTerminateByIDRequest.ProtoReflect.Descriptor instead. func (*PeeringTerminateByIDRequest) Descriptor() ([]byte, []int) { - return file_proto_pbpeering_peering_proto_rawDescGZIP(), []int{16} + return file_proto_pbpeering_peering_proto_rawDescGZIP(), []int{17} } func (x *PeeringTerminateByIDRequest) GetID() string { @@ -1258,7 +1307,7 @@ type PeeringTerminateByIDResponse struct { func (x *PeeringTerminateByIDResponse) Reset() { *x = PeeringTerminateByIDResponse{} if protoimpl.UnsafeEnabled { - mi := &file_proto_pbpeering_peering_proto_msgTypes[17] + mi := &file_proto_pbpeering_peering_proto_msgTypes[18] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1271,7 +1320,7 @@ func (x *PeeringTerminateByIDResponse) String() string { func (*PeeringTerminateByIDResponse) ProtoMessage() {} func (x *PeeringTerminateByIDResponse) ProtoReflect() protoreflect.Message { - mi := &file_proto_pbpeering_peering_proto_msgTypes[17] + mi := &file_proto_pbpeering_peering_proto_msgTypes[18] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1284,7 +1333,7 @@ func (x *PeeringTerminateByIDResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use PeeringTerminateByIDResponse.ProtoReflect.Descriptor instead. func (*PeeringTerminateByIDResponse) Descriptor() ([]byte, []int) { - return file_proto_pbpeering_peering_proto_rawDescGZIP(), []int{17} + return file_proto_pbpeering_peering_proto_rawDescGZIP(), []int{18} } type PeeringTrustBundleWriteRequest struct { @@ -1298,7 +1347,7 @@ type PeeringTrustBundleWriteRequest struct { func (x *PeeringTrustBundleWriteRequest) Reset() { *x = PeeringTrustBundleWriteRequest{} if protoimpl.UnsafeEnabled { - mi := &file_proto_pbpeering_peering_proto_msgTypes[18] + mi := &file_proto_pbpeering_peering_proto_msgTypes[19] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1311,7 +1360,7 @@ func (x *PeeringTrustBundleWriteRequest) String() string { func (*PeeringTrustBundleWriteRequest) ProtoMessage() {} func (x *PeeringTrustBundleWriteRequest) ProtoReflect() protoreflect.Message { - mi := &file_proto_pbpeering_peering_proto_msgTypes[18] + mi := &file_proto_pbpeering_peering_proto_msgTypes[19] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1324,7 +1373,7 @@ func (x *PeeringTrustBundleWriteRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use PeeringTrustBundleWriteRequest.ProtoReflect.Descriptor instead. func (*PeeringTrustBundleWriteRequest) Descriptor() ([]byte, []int) { - return file_proto_pbpeering_peering_proto_rawDescGZIP(), []int{18} + return file_proto_pbpeering_peering_proto_rawDescGZIP(), []int{19} } func (x *PeeringTrustBundleWriteRequest) GetPeeringTrustBundle() *PeeringTrustBundle { @@ -1343,7 +1392,7 @@ type PeeringTrustBundleWriteResponse struct { func (x *PeeringTrustBundleWriteResponse) Reset() { *x = PeeringTrustBundleWriteResponse{} if protoimpl.UnsafeEnabled { - mi := &file_proto_pbpeering_peering_proto_msgTypes[19] + mi := &file_proto_pbpeering_peering_proto_msgTypes[20] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1356,7 +1405,7 @@ func (x *PeeringTrustBundleWriteResponse) String() string { func (*PeeringTrustBundleWriteResponse) ProtoMessage() {} func (x *PeeringTrustBundleWriteResponse) ProtoReflect() protoreflect.Message { - mi := &file_proto_pbpeering_peering_proto_msgTypes[19] + mi := &file_proto_pbpeering_peering_proto_msgTypes[20] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1369,7 +1418,7 @@ func (x *PeeringTrustBundleWriteResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use PeeringTrustBundleWriteResponse.ProtoReflect.Descriptor instead. func (*PeeringTrustBundleWriteResponse) Descriptor() ([]byte, []int) { - return file_proto_pbpeering_peering_proto_rawDescGZIP(), []int{19} + return file_proto_pbpeering_peering_proto_rawDescGZIP(), []int{20} } type PeeringTrustBundleDeleteRequest struct { @@ -1384,7 +1433,7 @@ type PeeringTrustBundleDeleteRequest struct { func (x *PeeringTrustBundleDeleteRequest) Reset() { *x = PeeringTrustBundleDeleteRequest{} if protoimpl.UnsafeEnabled { - mi := &file_proto_pbpeering_peering_proto_msgTypes[20] + mi := &file_proto_pbpeering_peering_proto_msgTypes[21] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1397,7 +1446,7 @@ func (x *PeeringTrustBundleDeleteRequest) String() string { func (*PeeringTrustBundleDeleteRequest) ProtoMessage() {} func (x *PeeringTrustBundleDeleteRequest) ProtoReflect() protoreflect.Message { - mi := &file_proto_pbpeering_peering_proto_msgTypes[20] + mi := &file_proto_pbpeering_peering_proto_msgTypes[21] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1410,7 +1459,7 @@ func (x *PeeringTrustBundleDeleteRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use PeeringTrustBundleDeleteRequest.ProtoReflect.Descriptor instead. func (*PeeringTrustBundleDeleteRequest) Descriptor() ([]byte, []int) { - return file_proto_pbpeering_peering_proto_rawDescGZIP(), []int{20} + return file_proto_pbpeering_peering_proto_rawDescGZIP(), []int{21} } func (x *PeeringTrustBundleDeleteRequest) GetName() string { @@ -1436,7 +1485,7 @@ type PeeringTrustBundleDeleteResponse struct { func (x *PeeringTrustBundleDeleteResponse) Reset() { *x = PeeringTrustBundleDeleteResponse{} if protoimpl.UnsafeEnabled { - mi := &file_proto_pbpeering_peering_proto_msgTypes[21] + mi := &file_proto_pbpeering_peering_proto_msgTypes[22] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1449,7 +1498,7 @@ func (x *PeeringTrustBundleDeleteResponse) String() string { func (*PeeringTrustBundleDeleteResponse) ProtoMessage() {} func (x *PeeringTrustBundleDeleteResponse) ProtoReflect() protoreflect.Message { - mi := &file_proto_pbpeering_peering_proto_msgTypes[21] + mi := &file_proto_pbpeering_peering_proto_msgTypes[22] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1462,7 +1511,7 @@ func (x *PeeringTrustBundleDeleteResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use PeeringTrustBundleDeleteResponse.ProtoReflect.Descriptor instead. func (*PeeringTrustBundleDeleteResponse) Descriptor() ([]byte, []int) { - return file_proto_pbpeering_peering_proto_rawDescGZIP(), []int{21} + return file_proto_pbpeering_peering_proto_rawDescGZIP(), []int{22} } // mog annotation: @@ -1490,7 +1539,7 @@ type GenerateTokenRequest struct { func (x *GenerateTokenRequest) Reset() { *x = GenerateTokenRequest{} if protoimpl.UnsafeEnabled { - mi := &file_proto_pbpeering_peering_proto_msgTypes[22] + mi := &file_proto_pbpeering_peering_proto_msgTypes[23] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1503,7 +1552,7 @@ func (x *GenerateTokenRequest) String() string { func (*GenerateTokenRequest) ProtoMessage() {} func (x *GenerateTokenRequest) ProtoReflect() protoreflect.Message { - mi := &file_proto_pbpeering_peering_proto_msgTypes[22] + mi := &file_proto_pbpeering_peering_proto_msgTypes[23] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1516,7 +1565,7 @@ func (x *GenerateTokenRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use GenerateTokenRequest.ProtoReflect.Descriptor instead. func (*GenerateTokenRequest) Descriptor() ([]byte, []int) { - return file_proto_pbpeering_peering_proto_rawDescGZIP(), []int{22} + return file_proto_pbpeering_peering_proto_rawDescGZIP(), []int{23} } func (x *GenerateTokenRequest) GetPeerName() string { @@ -1565,7 +1614,7 @@ type GenerateTokenResponse struct { func (x *GenerateTokenResponse) Reset() { *x = GenerateTokenResponse{} if protoimpl.UnsafeEnabled { - mi := &file_proto_pbpeering_peering_proto_msgTypes[23] + mi := &file_proto_pbpeering_peering_proto_msgTypes[24] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1578,7 +1627,7 @@ func (x *GenerateTokenResponse) String() string { func (*GenerateTokenResponse) ProtoMessage() {} func (x *GenerateTokenResponse) ProtoReflect() protoreflect.Message { - mi := &file_proto_pbpeering_peering_proto_msgTypes[23] + mi := &file_proto_pbpeering_peering_proto_msgTypes[24] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1591,7 +1640,7 @@ func (x *GenerateTokenResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use GenerateTokenResponse.ProtoReflect.Descriptor instead. func (*GenerateTokenResponse) Descriptor() ([]byte, []int) { - return file_proto_pbpeering_peering_proto_rawDescGZIP(), []int{23} + return file_proto_pbpeering_peering_proto_rawDescGZIP(), []int{24} } func (x *GenerateTokenResponse) GetPeeringToken() string { @@ -1624,7 +1673,7 @@ type EstablishRequest struct { func (x *EstablishRequest) Reset() { *x = EstablishRequest{} if protoimpl.UnsafeEnabled { - mi := &file_proto_pbpeering_peering_proto_msgTypes[24] + mi := &file_proto_pbpeering_peering_proto_msgTypes[25] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1637,7 +1686,7 @@ func (x *EstablishRequest) String() string { func (*EstablishRequest) ProtoMessage() {} func (x *EstablishRequest) ProtoReflect() protoreflect.Message { - mi := &file_proto_pbpeering_peering_proto_msgTypes[24] + mi := &file_proto_pbpeering_peering_proto_msgTypes[25] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1650,7 +1699,7 @@ func (x *EstablishRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use EstablishRequest.ProtoReflect.Descriptor instead. func (*EstablishRequest) Descriptor() ([]byte, []int) { - return file_proto_pbpeering_peering_proto_rawDescGZIP(), []int{24} + return file_proto_pbpeering_peering_proto_rawDescGZIP(), []int{25} } func (x *EstablishRequest) GetPeerName() string { @@ -1695,7 +1744,7 @@ type EstablishResponse struct { func (x *EstablishResponse) Reset() { *x = EstablishResponse{} if protoimpl.UnsafeEnabled { - mi := &file_proto_pbpeering_peering_proto_msgTypes[25] + mi := &file_proto_pbpeering_peering_proto_msgTypes[26] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1708,7 +1757,7 @@ func (x *EstablishResponse) String() string { func (*EstablishResponse) ProtoMessage() {} func (x *EstablishResponse) ProtoReflect() protoreflect.Message { - mi := &file_proto_pbpeering_peering_proto_msgTypes[25] + mi := &file_proto_pbpeering_peering_proto_msgTypes[26] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1721,7 +1770,7 @@ func (x *EstablishResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use EstablishResponse.ProtoReflect.Descriptor instead. func (*EstablishResponse) Descriptor() ([]byte, []int) { - return file_proto_pbpeering_peering_proto_rawDescGZIP(), []int{25} + return file_proto_pbpeering_peering_proto_rawDescGZIP(), []int{26} } // GenerateTokenRequest encodes a request to persist a peering establishment @@ -1739,7 +1788,7 @@ type SecretsWriteRequest_GenerateTokenRequest struct { func (x *SecretsWriteRequest_GenerateTokenRequest) Reset() { *x = SecretsWriteRequest_GenerateTokenRequest{} if protoimpl.UnsafeEnabled { - mi := &file_proto_pbpeering_peering_proto_msgTypes[26] + mi := &file_proto_pbpeering_peering_proto_msgTypes[27] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1752,7 +1801,7 @@ func (x *SecretsWriteRequest_GenerateTokenRequest) String() string { func (*SecretsWriteRequest_GenerateTokenRequest) ProtoMessage() {} func (x *SecretsWriteRequest_GenerateTokenRequest) ProtoReflect() protoreflect.Message { - mi := &file_proto_pbpeering_peering_proto_msgTypes[26] + mi := &file_proto_pbpeering_peering_proto_msgTypes[27] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1793,7 +1842,7 @@ type SecretsWriteRequest_ExchangeSecretRequest struct { func (x *SecretsWriteRequest_ExchangeSecretRequest) Reset() { *x = SecretsWriteRequest_ExchangeSecretRequest{} if protoimpl.UnsafeEnabled { - mi := &file_proto_pbpeering_peering_proto_msgTypes[27] + mi := &file_proto_pbpeering_peering_proto_msgTypes[28] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1806,7 +1855,7 @@ func (x *SecretsWriteRequest_ExchangeSecretRequest) String() string { func (*SecretsWriteRequest_ExchangeSecretRequest) ProtoMessage() {} func (x *SecretsWriteRequest_ExchangeSecretRequest) ProtoReflect() protoreflect.Message { - mi := &file_proto_pbpeering_peering_proto_msgTypes[27] + mi := &file_proto_pbpeering_peering_proto_msgTypes[28] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1852,7 +1901,7 @@ type SecretsWriteRequest_PromotePendingRequest struct { func (x *SecretsWriteRequest_PromotePendingRequest) Reset() { *x = SecretsWriteRequest_PromotePendingRequest{} if protoimpl.UnsafeEnabled { - mi := &file_proto_pbpeering_peering_proto_msgTypes[28] + mi := &file_proto_pbpeering_peering_proto_msgTypes[29] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1865,7 +1914,7 @@ func (x *SecretsWriteRequest_PromotePendingRequest) String() string { func (*SecretsWriteRequest_PromotePendingRequest) ProtoMessage() {} func (x *SecretsWriteRequest_PromotePendingRequest) ProtoReflect() protoreflect.Message { - mi := &file_proto_pbpeering_peering_proto_msgTypes[28] + mi := &file_proto_pbpeering_peering_proto_msgTypes[29] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1904,7 +1953,7 @@ type SecretsWriteRequest_EstablishRequest struct { func (x *SecretsWriteRequest_EstablishRequest) Reset() { *x = SecretsWriteRequest_EstablishRequest{} if protoimpl.UnsafeEnabled { - mi := &file_proto_pbpeering_peering_proto_msgTypes[29] + mi := &file_proto_pbpeering_peering_proto_msgTypes[30] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1917,7 +1966,7 @@ func (x *SecretsWriteRequest_EstablishRequest) String() string { func (*SecretsWriteRequest_EstablishRequest) ProtoMessage() {} func (x *SecretsWriteRequest_EstablishRequest) ProtoReflect() protoreflect.Message { - mi := &file_proto_pbpeering_peering_proto_msgTypes[29] + mi := &file_proto_pbpeering_peering_proto_msgTypes[30] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1952,7 +2001,7 @@ type PeeringSecrets_Establishment struct { func (x *PeeringSecrets_Establishment) Reset() { *x = PeeringSecrets_Establishment{} if protoimpl.UnsafeEnabled { - mi := &file_proto_pbpeering_peering_proto_msgTypes[30] + mi := &file_proto_pbpeering_peering_proto_msgTypes[31] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1965,7 +2014,7 @@ func (x *PeeringSecrets_Establishment) String() string { func (*PeeringSecrets_Establishment) ProtoMessage() {} func (x *PeeringSecrets_Establishment) ProtoReflect() protoreflect.Message { - mi := &file_proto_pbpeering_peering_proto_msgTypes[30] + mi := &file_proto_pbpeering_peering_proto_msgTypes[31] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2011,7 +2060,7 @@ type PeeringSecrets_Stream struct { func (x *PeeringSecrets_Stream) Reset() { *x = PeeringSecrets_Stream{} if protoimpl.UnsafeEnabled { - mi := &file_proto_pbpeering_peering_proto_msgTypes[31] + mi := &file_proto_pbpeering_peering_proto_msgTypes[32] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2024,7 +2073,7 @@ func (x *PeeringSecrets_Stream) String() string { func (*PeeringSecrets_Stream) ProtoMessage() {} func (x *PeeringSecrets_Stream) ProtoReflect() protoreflect.Message { - mi := &file_proto_pbpeering_peering_proto_msgTypes[31] + mi := &file_proto_pbpeering_peering_proto_msgTypes[32] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2197,241 +2246,244 @@ var file_proto_pbpeering_peering_proto_rawDesc = []byte{ 0x6e, 0x64, 0x65, 0x78, 0x18, 0x06, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x12, 0x20, 0x0a, 0x0b, 0x4d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x07, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, 0x4d, 0x6f, - 0x64, 0x69, 0x66, 0x79, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x22, 0x46, 0x0a, 0x12, 0x50, 0x65, 0x65, - 0x72, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x61, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, - 0x12, 0x0a, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x4e, - 0x61, 0x6d, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x50, 0x61, 0x72, 0x74, 0x69, 0x74, 0x69, 0x6f, 0x6e, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x50, 0x61, 0x72, 0x74, 0x69, 0x74, 0x69, 0x6f, - 0x6e, 0x22, 0x5b, 0x0a, 0x13, 0x50, 0x65, 0x65, 0x72, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x61, 0x64, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x44, 0x0a, 0x07, 0x50, 0x65, 0x65, 0x72, - 0x69, 0x6e, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x68, 0x61, 0x73, 0x68, - 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2e, 0x69, 0x6e, 0x74, - 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2e, 0x70, 0x65, 0x65, 0x72, 0x69, 0x6e, 0x67, 0x2e, 0x50, 0x65, - 0x65, 0x72, 0x69, 0x6e, 0x67, 0x52, 0x07, 0x50, 0x65, 0x65, 0x72, 0x69, 0x6e, 0x67, 0x22, 0x32, - 0x0a, 0x12, 0x50, 0x65, 0x65, 0x72, 0x69, 0x6e, 0x67, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x50, 0x61, 0x72, 0x74, 0x69, 0x74, 0x69, 0x6f, - 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x50, 0x61, 0x72, 0x74, 0x69, 0x74, 0x69, - 0x6f, 0x6e, 0x22, 0x5d, 0x0a, 0x13, 0x50, 0x65, 0x65, 0x72, 0x69, 0x6e, 0x67, 0x4c, 0x69, 0x73, - 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x46, 0x0a, 0x08, 0x50, 0x65, 0x65, - 0x72, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x68, 0x61, - 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2e, 0x69, - 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2e, 0x70, 0x65, 0x65, 0x72, 0x69, 0x6e, 0x67, 0x2e, - 0x50, 0x65, 0x65, 0x72, 0x69, 0x6e, 0x67, 0x52, 0x08, 0x50, 0x65, 0x65, 0x72, 0x69, 0x6e, 0x67, - 0x73, 0x22, 0xca, 0x02, 0x0a, 0x13, 0x50, 0x65, 0x65, 0x72, 0x69, 0x6e, 0x67, 0x57, 0x72, 0x69, - 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x44, 0x0a, 0x07, 0x50, 0x65, 0x65, - 0x72, 0x69, 0x6e, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x68, 0x61, 0x73, - 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2e, 0x69, 0x6e, - 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2e, 0x70, 0x65, 0x65, 0x72, 0x69, 0x6e, 0x67, 0x2e, 0x50, - 0x65, 0x65, 0x72, 0x69, 0x6e, 0x67, 0x52, 0x07, 0x50, 0x65, 0x65, 0x72, 0x69, 0x6e, 0x67, 0x12, - 0x5e, 0x0a, 0x0e, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x36, 0x2e, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, - 0x6f, 0x72, 0x70, 0x2e, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, - 0x6e, 0x61, 0x6c, 0x2e, 0x70, 0x65, 0x65, 0x72, 0x69, 0x6e, 0x67, 0x2e, 0x53, 0x65, 0x63, 0x72, - 0x65, 0x74, 0x73, 0x57, 0x72, 0x69, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x52, - 0x0e, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, - 0x54, 0x0a, 0x04, 0x4d, 0x65, 0x74, 0x61, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x40, 0x2e, - 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6c, - 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2e, 0x70, 0x65, 0x65, 0x72, 0x69, 0x6e, - 0x67, 0x2e, 0x50, 0x65, 0x65, 0x72, 0x69, 0x6e, 0x67, 0x57, 0x72, 0x69, 0x74, 0x65, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x4d, 0x65, 0x74, 0x61, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, - 0x04, 0x4d, 0x65, 0x74, 0x61, 0x1a, 0x37, 0x0a, 0x09, 0x4d, 0x65, 0x74, 0x61, 0x45, 0x6e, 0x74, - 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x16, - 0x0a, 0x14, 0x50, 0x65, 0x65, 0x72, 0x69, 0x6e, 0x67, 0x57, 0x72, 0x69, 0x74, 0x65, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x48, 0x0a, 0x14, 0x50, 0x65, 0x65, 0x72, 0x69, 0x6e, - 0x67, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, - 0x0a, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x4e, 0x61, - 0x6d, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x50, 0x61, 0x72, 0x74, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x50, 0x61, 0x72, 0x74, 0x69, 0x74, 0x69, 0x6f, 0x6e, - 0x22, 0x17, 0x0a, 0x15, 0x50, 0x65, 0x65, 0x72, 0x69, 0x6e, 0x67, 0x44, 0x65, 0x6c, 0x65, 0x74, - 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x93, 0x01, 0x0a, 0x1f, 0x54, 0x72, - 0x75, 0x73, 0x74, 0x42, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x42, 0x79, 0x53, - 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x20, 0x0a, - 0x0b, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x0b, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, - 0x1c, 0x0a, 0x09, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x09, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x1c, 0x0a, - 0x09, 0x50, 0x61, 0x72, 0x74, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x09, 0x50, 0x61, 0x72, 0x74, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x4b, - 0x69, 0x6e, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x4b, 0x69, 0x6e, 0x64, 0x22, - 0x89, 0x01, 0x0a, 0x20, 0x54, 0x72, 0x75, 0x73, 0x74, 0x42, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x4c, - 0x69, 0x73, 0x74, 0x42, 0x79, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x04, 0x52, 0x05, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x12, 0x4f, 0x0a, 0x07, 0x42, 0x75, - 0x6e, 0x64, 0x6c, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x35, 0x2e, 0x68, 0x61, - 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2e, 0x69, - 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2e, 0x70, 0x65, 0x65, 0x72, 0x69, 0x6e, 0x67, 0x2e, - 0x50, 0x65, 0x65, 0x72, 0x69, 0x6e, 0x67, 0x54, 0x72, 0x75, 0x73, 0x74, 0x42, 0x75, 0x6e, 0x64, - 0x6c, 0x65, 0x52, 0x07, 0x42, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x73, 0x22, 0x4a, 0x0a, 0x16, 0x54, - 0x72, 0x75, 0x73, 0x74, 0x42, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x52, 0x65, 0x61, 0x64, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x50, 0x61, 0x72, - 0x74, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x50, 0x61, - 0x72, 0x74, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x7e, 0x0a, 0x17, 0x54, 0x72, 0x75, 0x73, 0x74, - 0x42, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x52, 0x65, 0x61, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x04, 0x52, 0x05, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x12, 0x4d, 0x0a, 0x06, 0x42, 0x75, 0x6e, 0x64, - 0x6c, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x35, 0x2e, 0x68, 0x61, 0x73, 0x68, 0x69, - 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2e, 0x69, 0x6e, 0x74, 0x65, - 0x72, 0x6e, 0x61, 0x6c, 0x2e, 0x70, 0x65, 0x65, 0x72, 0x69, 0x6e, 0x67, 0x2e, 0x50, 0x65, 0x65, - 0x72, 0x69, 0x6e, 0x67, 0x54, 0x72, 0x75, 0x73, 0x74, 0x42, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x52, - 0x06, 0x42, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x22, 0x2d, 0x0a, 0x1b, 0x50, 0x65, 0x65, 0x72, 0x69, - 0x6e, 0x67, 0x54, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x61, 0x74, 0x65, 0x42, 0x79, 0x49, 0x44, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x02, 0x49, 0x44, 0x22, 0x1e, 0x0a, 0x1c, 0x50, 0x65, 0x65, 0x72, 0x69, 0x6e, - 0x67, 0x54, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x61, 0x74, 0x65, 0x42, 0x79, 0x49, 0x44, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x87, 0x01, 0x0a, 0x1e, 0x50, 0x65, 0x65, 0x72, 0x69, - 0x6e, 0x67, 0x54, 0x72, 0x75, 0x73, 0x74, 0x42, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x57, 0x72, 0x69, - 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x65, 0x0a, 0x12, 0x50, 0x65, 0x65, - 0x72, 0x69, 0x6e, 0x67, 0x54, 0x72, 0x75, 0x73, 0x74, 0x42, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x35, 0x2e, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, + 0x64, 0x69, 0x66, 0x79, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x22, 0x36, 0x0a, 0x16, 0x50, 0x65, 0x65, + 0x72, 0x69, 0x6e, 0x67, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, + 0x73, 0x65, 0x73, 0x12, 0x1c, 0x0a, 0x09, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, + 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x09, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, + 0x73, 0x22, 0x46, 0x0a, 0x12, 0x50, 0x65, 0x65, 0x72, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x61, 0x64, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x50, + 0x61, 0x72, 0x74, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, + 0x50, 0x61, 0x72, 0x74, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x5b, 0x0a, 0x13, 0x50, 0x65, 0x65, + 0x72, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x61, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x12, 0x44, 0x0a, 0x07, 0x50, 0x65, 0x65, 0x72, 0x69, 0x6e, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x2a, 0x2e, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x63, 0x6f, + 0x6e, 0x73, 0x75, 0x6c, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2e, 0x70, 0x65, + 0x65, 0x72, 0x69, 0x6e, 0x67, 0x2e, 0x50, 0x65, 0x65, 0x72, 0x69, 0x6e, 0x67, 0x52, 0x07, 0x50, + 0x65, 0x65, 0x72, 0x69, 0x6e, 0x67, 0x22, 0x32, 0x0a, 0x12, 0x50, 0x65, 0x65, 0x72, 0x69, 0x6e, + 0x67, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1c, 0x0a, 0x09, + 0x50, 0x61, 0x72, 0x74, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x09, 0x50, 0x61, 0x72, 0x74, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x5d, 0x0a, 0x13, 0x50, 0x65, + 0x65, 0x72, 0x69, 0x6e, 0x67, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x12, 0x46, 0x0a, 0x08, 0x50, 0x65, 0x65, 0x72, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x01, 0x20, + 0x03, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2e, + 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2e, + 0x70, 0x65, 0x65, 0x72, 0x69, 0x6e, 0x67, 0x2e, 0x50, 0x65, 0x65, 0x72, 0x69, 0x6e, 0x67, 0x52, + 0x08, 0x50, 0x65, 0x65, 0x72, 0x69, 0x6e, 0x67, 0x73, 0x22, 0xca, 0x02, 0x0a, 0x13, 0x50, 0x65, + 0x65, 0x72, 0x69, 0x6e, 0x67, 0x57, 0x72, 0x69, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x12, 0x44, 0x0a, 0x07, 0x50, 0x65, 0x65, 0x72, 0x69, 0x6e, 0x67, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x63, + 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2e, 0x70, + 0x65, 0x65, 0x72, 0x69, 0x6e, 0x67, 0x2e, 0x50, 0x65, 0x65, 0x72, 0x69, 0x6e, 0x67, 0x52, 0x07, + 0x50, 0x65, 0x65, 0x72, 0x69, 0x6e, 0x67, 0x12, 0x5e, 0x0a, 0x0e, 0x53, 0x65, 0x63, 0x72, 0x65, + 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x36, 0x2e, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x63, 0x6f, 0x6e, 0x73, + 0x75, 0x6c, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2e, 0x70, 0x65, 0x65, 0x72, + 0x69, 0x6e, 0x67, 0x2e, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x73, 0x57, 0x72, 0x69, 0x74, 0x65, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x52, 0x0e, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x73, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x54, 0x0a, 0x04, 0x4d, 0x65, 0x74, 0x61, 0x18, + 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x40, 0x2e, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2e, 0x70, 0x65, 0x65, 0x72, 0x69, 0x6e, 0x67, 0x2e, 0x50, 0x65, 0x65, 0x72, 0x69, 0x6e, - 0x67, 0x54, 0x72, 0x75, 0x73, 0x74, 0x42, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x52, 0x12, 0x50, 0x65, - 0x65, 0x72, 0x69, 0x6e, 0x67, 0x54, 0x72, 0x75, 0x73, 0x74, 0x42, 0x75, 0x6e, 0x64, 0x6c, 0x65, - 0x22, 0x21, 0x0a, 0x1f, 0x50, 0x65, 0x65, 0x72, 0x69, 0x6e, 0x67, 0x54, 0x72, 0x75, 0x73, 0x74, - 0x42, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x57, 0x72, 0x69, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x22, 0x53, 0x0a, 0x1f, 0x50, 0x65, 0x65, 0x72, 0x69, 0x6e, 0x67, 0x54, 0x72, - 0x75, 0x73, 0x74, 0x42, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, + 0x67, 0x57, 0x72, 0x69, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x4d, 0x65, + 0x74, 0x61, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x04, 0x4d, 0x65, 0x74, 0x61, 0x1a, 0x37, 0x0a, + 0x09, 0x4d, 0x65, 0x74, 0x61, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, + 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, + 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, + 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x16, 0x0a, 0x14, 0x50, 0x65, 0x65, 0x72, 0x69, 0x6e, + 0x67, 0x57, 0x72, 0x69, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x48, + 0x0a, 0x14, 0x50, 0x65, 0x65, 0x72, 0x69, 0x6e, 0x67, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x50, 0x61, 0x72, 0x74, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x50, - 0x61, 0x72, 0x74, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x22, 0x0a, 0x20, 0x50, 0x65, 0x65, 0x72, - 0x69, 0x6e, 0x67, 0x54, 0x72, 0x75, 0x73, 0x74, 0x42, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x44, 0x65, - 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x9a, 0x02, 0x0a, - 0x14, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x50, 0x65, 0x65, 0x72, 0x4e, 0x61, 0x6d, - 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x50, 0x65, 0x65, 0x72, 0x4e, 0x61, 0x6d, + 0x61, 0x72, 0x74, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x17, 0x0a, 0x15, 0x50, 0x65, 0x65, 0x72, + 0x69, 0x6e, 0x67, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x22, 0x93, 0x01, 0x0a, 0x1f, 0x54, 0x72, 0x75, 0x73, 0x74, 0x42, 0x75, 0x6e, 0x64, 0x6c, + 0x65, 0x4c, 0x69, 0x73, 0x74, 0x42, 0x79, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x20, 0x0a, 0x0b, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, + 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x53, 0x65, 0x72, 0x76, + 0x69, 0x63, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x4e, 0x61, 0x6d, 0x65, 0x73, + 0x70, 0x61, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x4e, 0x61, 0x6d, 0x65, + 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x50, 0x61, 0x72, 0x74, 0x69, 0x74, 0x69, + 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x50, 0x61, 0x72, 0x74, 0x69, 0x74, + 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x4b, 0x69, 0x6e, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x04, 0x4b, 0x69, 0x6e, 0x64, 0x22, 0x89, 0x01, 0x0a, 0x20, 0x54, 0x72, 0x75, 0x73, + 0x74, 0x42, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x42, 0x79, 0x53, 0x65, 0x72, + 0x76, 0x69, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x14, 0x0a, 0x05, + 0x49, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x05, 0x49, 0x6e, 0x64, + 0x65, 0x78, 0x12, 0x4f, 0x0a, 0x07, 0x42, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x73, 0x18, 0x02, 0x20, + 0x03, 0x28, 0x0b, 0x32, 0x35, 0x2e, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2e, + 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2e, + 0x70, 0x65, 0x65, 0x72, 0x69, 0x6e, 0x67, 0x2e, 0x50, 0x65, 0x65, 0x72, 0x69, 0x6e, 0x67, 0x54, + 0x72, 0x75, 0x73, 0x74, 0x42, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x52, 0x07, 0x42, 0x75, 0x6e, 0x64, + 0x6c, 0x65, 0x73, 0x22, 0x4a, 0x0a, 0x16, 0x54, 0x72, 0x75, 0x73, 0x74, 0x42, 0x75, 0x6e, 0x64, + 0x6c, 0x65, 0x52, 0x65, 0x61, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, + 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x50, 0x61, 0x72, 0x74, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x50, 0x61, 0x72, 0x74, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, - 0x55, 0x0a, 0x04, 0x4d, 0x65, 0x74, 0x61, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x41, 0x2e, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x50, 0x61, 0x72, 0x74, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x22, + 0x7e, 0x0a, 0x17, 0x54, 0x72, 0x75, 0x73, 0x74, 0x42, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x52, 0x65, + 0x61, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x49, 0x6e, + 0x64, 0x65, 0x78, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x05, 0x49, 0x6e, 0x64, 0x65, 0x78, + 0x12, 0x4d, 0x0a, 0x06, 0x42, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x35, 0x2e, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x63, 0x6f, 0x6e, + 0x73, 0x75, 0x6c, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2e, 0x70, 0x65, 0x65, + 0x72, 0x69, 0x6e, 0x67, 0x2e, 0x50, 0x65, 0x65, 0x72, 0x69, 0x6e, 0x67, 0x54, 0x72, 0x75, 0x73, + 0x74, 0x42, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x52, 0x06, 0x42, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x22, + 0x2d, 0x0a, 0x1b, 0x50, 0x65, 0x65, 0x72, 0x69, 0x6e, 0x67, 0x54, 0x65, 0x72, 0x6d, 0x69, 0x6e, + 0x61, 0x74, 0x65, 0x42, 0x79, 0x49, 0x44, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, + 0x0a, 0x02, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x49, 0x44, 0x22, 0x1e, + 0x0a, 0x1c, 0x50, 0x65, 0x65, 0x72, 0x69, 0x6e, 0x67, 0x54, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x61, + 0x74, 0x65, 0x42, 0x79, 0x49, 0x44, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x87, + 0x01, 0x0a, 0x1e, 0x50, 0x65, 0x65, 0x72, 0x69, 0x6e, 0x67, 0x54, 0x72, 0x75, 0x73, 0x74, 0x42, + 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x57, 0x72, 0x69, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x12, 0x65, 0x0a, 0x12, 0x50, 0x65, 0x65, 0x72, 0x69, 0x6e, 0x67, 0x54, 0x72, 0x75, 0x73, + 0x74, 0x42, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x35, 0x2e, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2e, 0x70, 0x65, 0x65, 0x72, 0x69, 0x6e, - 0x67, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x4d, 0x65, 0x74, 0x61, 0x45, 0x6e, 0x74, 0x72, 0x79, - 0x52, 0x04, 0x4d, 0x65, 0x74, 0x61, 0x12, 0x38, 0x0a, 0x17, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, - 0x45, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, - 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x09, 0x52, 0x17, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x45, - 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, - 0x1a, 0x37, 0x0a, 0x09, 0x4d, 0x65, 0x74, 0x61, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, - 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, - 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, - 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x3b, 0x0a, 0x15, 0x47, 0x65, 0x6e, - 0x65, 0x72, 0x61, 0x74, 0x65, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x12, 0x22, 0x0a, 0x0c, 0x50, 0x65, 0x65, 0x72, 0x69, 0x6e, 0x67, 0x54, 0x6f, 0x6b, - 0x65, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x50, 0x65, 0x65, 0x72, 0x69, 0x6e, - 0x67, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x22, 0xfc, 0x01, 0x0a, 0x10, 0x45, 0x73, 0x74, 0x61, 0x62, - 0x6c, 0x69, 0x73, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x50, - 0x65, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x50, - 0x65, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x22, 0x0a, 0x0c, 0x50, 0x65, 0x65, 0x72, 0x69, - 0x6e, 0x67, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x50, - 0x65, 0x65, 0x72, 0x69, 0x6e, 0x67, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x1c, 0x0a, 0x09, 0x50, - 0x61, 0x72, 0x74, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, - 0x50, 0x61, 0x72, 0x74, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x51, 0x0a, 0x04, 0x4d, 0x65, 0x74, - 0x61, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x3d, 0x2e, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, - 0x6f, 0x72, 0x70, 0x2e, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, - 0x6e, 0x61, 0x6c, 0x2e, 0x70, 0x65, 0x65, 0x72, 0x69, 0x6e, 0x67, 0x2e, 0x45, 0x73, 0x74, 0x61, - 0x62, 0x6c, 0x69, 0x73, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x4d, 0x65, 0x74, - 0x61, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x04, 0x4d, 0x65, 0x74, 0x61, 0x1a, 0x37, 0x0a, 0x09, - 0x4d, 0x65, 0x74, 0x61, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, - 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, - 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x13, 0x0a, 0x11, 0x45, 0x73, 0x74, 0x61, 0x62, 0x6c, 0x69, - 0x73, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2a, 0x73, 0x0a, 0x0c, 0x50, 0x65, - 0x65, 0x72, 0x69, 0x6e, 0x67, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x0d, 0x0a, 0x09, 0x55, 0x4e, - 0x44, 0x45, 0x46, 0x49, 0x4e, 0x45, 0x44, 0x10, 0x00, 0x12, 0x0b, 0x0a, 0x07, 0x50, 0x45, 0x4e, - 0x44, 0x49, 0x4e, 0x47, 0x10, 0x01, 0x12, 0x10, 0x0a, 0x0c, 0x45, 0x53, 0x54, 0x41, 0x42, 0x4c, - 0x49, 0x53, 0x48, 0x49, 0x4e, 0x47, 0x10, 0x02, 0x12, 0x0a, 0x0a, 0x06, 0x41, 0x43, 0x54, 0x49, - 0x56, 0x45, 0x10, 0x03, 0x12, 0x0b, 0x0a, 0x07, 0x46, 0x41, 0x49, 0x4c, 0x49, 0x4e, 0x47, 0x10, - 0x04, 0x12, 0x0c, 0x0a, 0x08, 0x44, 0x45, 0x4c, 0x45, 0x54, 0x49, 0x4e, 0x47, 0x10, 0x05, 0x12, - 0x0e, 0x0a, 0x0a, 0x54, 0x45, 0x52, 0x4d, 0x49, 0x4e, 0x41, 0x54, 0x45, 0x44, 0x10, 0x06, 0x32, - 0xc0, 0x08, 0x0a, 0x0e, 0x50, 0x65, 0x65, 0x72, 0x69, 0x6e, 0x67, 0x53, 0x65, 0x72, 0x76, 0x69, - 0x63, 0x65, 0x12, 0x82, 0x01, 0x0a, 0x0d, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x54, - 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x37, 0x2e, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, - 0x2e, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, - 0x2e, 0x70, 0x65, 0x65, 0x72, 0x69, 0x6e, 0x67, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, - 0x65, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x38, 0x2e, - 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6c, - 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2e, 0x70, 0x65, 0x65, 0x72, 0x69, 0x6e, - 0x67, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x76, 0x0a, 0x09, 0x45, 0x73, 0x74, 0x61, 0x62, - 0x6c, 0x69, 0x73, 0x68, 0x12, 0x33, 0x2e, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, - 0x2e, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, - 0x2e, 0x70, 0x65, 0x65, 0x72, 0x69, 0x6e, 0x67, 0x2e, 0x45, 0x73, 0x74, 0x61, 0x62, 0x6c, 0x69, - 0x73, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x34, 0x2e, 0x68, 0x61, 0x73, 0x68, - 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2e, 0x69, 0x6e, 0x74, - 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2e, 0x70, 0x65, 0x65, 0x72, 0x69, 0x6e, 0x67, 0x2e, 0x45, 0x73, - 0x74, 0x61, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, - 0x7c, 0x0a, 0x0b, 0x50, 0x65, 0x65, 0x72, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x61, 0x64, 0x12, 0x35, - 0x2e, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x63, 0x6f, 0x6e, 0x73, 0x75, - 0x6c, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2e, 0x70, 0x65, 0x65, 0x72, 0x69, - 0x6e, 0x67, 0x2e, 0x50, 0x65, 0x65, 0x72, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x61, 0x64, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x36, 0x2e, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, + 0x67, 0x2e, 0x50, 0x65, 0x65, 0x72, 0x69, 0x6e, 0x67, 0x54, 0x72, 0x75, 0x73, 0x74, 0x42, 0x75, + 0x6e, 0x64, 0x6c, 0x65, 0x52, 0x12, 0x50, 0x65, 0x65, 0x72, 0x69, 0x6e, 0x67, 0x54, 0x72, 0x75, + 0x73, 0x74, 0x42, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x22, 0x21, 0x0a, 0x1f, 0x50, 0x65, 0x65, 0x72, + 0x69, 0x6e, 0x67, 0x54, 0x72, 0x75, 0x73, 0x74, 0x42, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x57, 0x72, + 0x69, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x53, 0x0a, 0x1f, 0x50, + 0x65, 0x65, 0x72, 0x69, 0x6e, 0x67, 0x54, 0x72, 0x75, 0x73, 0x74, 0x42, 0x75, 0x6e, 0x64, 0x6c, + 0x65, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, + 0x0a, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x4e, 0x61, + 0x6d, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x50, 0x61, 0x72, 0x74, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x50, 0x61, 0x72, 0x74, 0x69, 0x74, 0x69, 0x6f, 0x6e, + 0x22, 0x22, 0x0a, 0x20, 0x50, 0x65, 0x65, 0x72, 0x69, 0x6e, 0x67, 0x54, 0x72, 0x75, 0x73, 0x74, + 0x42, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x9a, 0x02, 0x0a, 0x14, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, + 0x65, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, + 0x08, 0x50, 0x65, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x08, 0x50, 0x65, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x50, 0x61, 0x72, + 0x74, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x50, 0x61, + 0x72, 0x74, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x55, 0x0a, 0x04, 0x4d, 0x65, 0x74, 0x61, 0x18, + 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x41, 0x2e, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, - 0x6c, 0x2e, 0x70, 0x65, 0x65, 0x72, 0x69, 0x6e, 0x67, 0x2e, 0x50, 0x65, 0x65, 0x72, 0x69, 0x6e, - 0x67, 0x52, 0x65, 0x61, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x7c, 0x0a, - 0x0b, 0x50, 0x65, 0x65, 0x72, 0x69, 0x6e, 0x67, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x35, 0x2e, 0x68, + 0x6c, 0x2e, 0x70, 0x65, 0x65, 0x72, 0x69, 0x6e, 0x67, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, + 0x74, 0x65, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x4d, + 0x65, 0x74, 0x61, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x04, 0x4d, 0x65, 0x74, 0x61, 0x12, 0x38, + 0x0a, 0x17, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x45, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, + 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x09, 0x52, + 0x17, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x45, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x41, + 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x1a, 0x37, 0x0a, 0x09, 0x4d, 0x65, 0x74, 0x61, + 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, + 0x01, 0x22, 0x3b, 0x0a, 0x15, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x54, 0x6f, 0x6b, + 0x65, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x22, 0x0a, 0x0c, 0x50, 0x65, + 0x65, 0x72, 0x69, 0x6e, 0x67, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x0c, 0x50, 0x65, 0x65, 0x72, 0x69, 0x6e, 0x67, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x22, 0xfc, + 0x01, 0x0a, 0x10, 0x45, 0x73, 0x74, 0x61, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x50, 0x65, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x50, 0x65, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x12, + 0x22, 0x0a, 0x0c, 0x50, 0x65, 0x65, 0x72, 0x69, 0x6e, 0x67, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x50, 0x65, 0x65, 0x72, 0x69, 0x6e, 0x67, 0x54, 0x6f, + 0x6b, 0x65, 0x6e, 0x12, 0x1c, 0x0a, 0x09, 0x50, 0x61, 0x72, 0x74, 0x69, 0x74, 0x69, 0x6f, 0x6e, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x50, 0x61, 0x72, 0x74, 0x69, 0x74, 0x69, 0x6f, + 0x6e, 0x12, 0x51, 0x0a, 0x04, 0x4d, 0x65, 0x74, 0x61, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, + 0x3d, 0x2e, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x63, 0x6f, 0x6e, 0x73, + 0x75, 0x6c, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2e, 0x70, 0x65, 0x65, 0x72, + 0x69, 0x6e, 0x67, 0x2e, 0x45, 0x73, 0x74, 0x61, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x2e, 0x4d, 0x65, 0x74, 0x61, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x04, + 0x4d, 0x65, 0x74, 0x61, 0x1a, 0x37, 0x0a, 0x09, 0x4d, 0x65, 0x74, 0x61, 0x45, 0x6e, 0x74, 0x72, + 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, + 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x13, 0x0a, + 0x11, 0x45, 0x73, 0x74, 0x61, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x2a, 0x73, 0x0a, 0x0c, 0x50, 0x65, 0x65, 0x72, 0x69, 0x6e, 0x67, 0x53, 0x74, 0x61, + 0x74, 0x65, 0x12, 0x0d, 0x0a, 0x09, 0x55, 0x4e, 0x44, 0x45, 0x46, 0x49, 0x4e, 0x45, 0x44, 0x10, + 0x00, 0x12, 0x0b, 0x0a, 0x07, 0x50, 0x45, 0x4e, 0x44, 0x49, 0x4e, 0x47, 0x10, 0x01, 0x12, 0x10, + 0x0a, 0x0c, 0x45, 0x53, 0x54, 0x41, 0x42, 0x4c, 0x49, 0x53, 0x48, 0x49, 0x4e, 0x47, 0x10, 0x02, + 0x12, 0x0a, 0x0a, 0x06, 0x41, 0x43, 0x54, 0x49, 0x56, 0x45, 0x10, 0x03, 0x12, 0x0b, 0x0a, 0x07, + 0x46, 0x41, 0x49, 0x4c, 0x49, 0x4e, 0x47, 0x10, 0x04, 0x12, 0x0c, 0x0a, 0x08, 0x44, 0x45, 0x4c, + 0x45, 0x54, 0x49, 0x4e, 0x47, 0x10, 0x05, 0x12, 0x0e, 0x0a, 0x0a, 0x54, 0x45, 0x52, 0x4d, 0x49, + 0x4e, 0x41, 0x54, 0x45, 0x44, 0x10, 0x06, 0x32, 0xc0, 0x08, 0x0a, 0x0e, 0x50, 0x65, 0x65, 0x72, + 0x69, 0x6e, 0x67, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x82, 0x01, 0x0a, 0x0d, 0x47, + 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x37, 0x2e, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2e, 0x70, 0x65, 0x65, 0x72, 0x69, 0x6e, 0x67, - 0x2e, 0x50, 0x65, 0x65, 0x72, 0x69, 0x6e, 0x67, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x36, 0x2e, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2e, - 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2e, - 0x70, 0x65, 0x65, 0x72, 0x69, 0x6e, 0x67, 0x2e, 0x50, 0x65, 0x65, 0x72, 0x69, 0x6e, 0x67, 0x4c, - 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x82, 0x01, 0x0a, 0x0d, - 0x50, 0x65, 0x65, 0x72, 0x69, 0x6e, 0x67, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x12, 0x37, 0x2e, - 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6c, - 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2e, 0x70, 0x65, 0x65, 0x72, 0x69, 0x6e, - 0x67, 0x2e, 0x50, 0x65, 0x65, 0x72, 0x69, 0x6e, 0x67, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x38, 0x2e, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, + 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x38, 0x2e, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, + 0x70, 0x2e, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, + 0x6c, 0x2e, 0x70, 0x65, 0x65, 0x72, 0x69, 0x6e, 0x67, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, + 0x74, 0x65, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, + 0x76, 0x0a, 0x09, 0x45, 0x73, 0x74, 0x61, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x12, 0x33, 0x2e, 0x68, + 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2e, + 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2e, 0x70, 0x65, 0x65, 0x72, 0x69, 0x6e, 0x67, + 0x2e, 0x45, 0x73, 0x74, 0x61, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x34, 0x2e, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x63, 0x6f, + 0x6e, 0x73, 0x75, 0x6c, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2e, 0x70, 0x65, + 0x65, 0x72, 0x69, 0x6e, 0x67, 0x2e, 0x45, 0x73, 0x74, 0x61, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x7c, 0x0a, 0x0b, 0x50, 0x65, 0x65, 0x72, 0x69, + 0x6e, 0x67, 0x52, 0x65, 0x61, 0x64, 0x12, 0x35, 0x2e, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2e, 0x70, 0x65, 0x65, 0x72, 0x69, 0x6e, 0x67, 0x2e, 0x50, 0x65, 0x65, 0x72, 0x69, - 0x6e, 0x67, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x12, 0x7f, 0x0a, 0x0c, 0x50, 0x65, 0x65, 0x72, 0x69, 0x6e, 0x67, 0x57, 0x72, 0x69, 0x74, 0x65, - 0x12, 0x36, 0x2e, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x63, 0x6f, 0x6e, - 0x73, 0x75, 0x6c, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2e, 0x70, 0x65, 0x65, - 0x72, 0x69, 0x6e, 0x67, 0x2e, 0x50, 0x65, 0x65, 0x72, 0x69, 0x6e, 0x67, 0x57, 0x72, 0x69, 0x74, - 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x37, 0x2e, 0x68, 0x61, 0x73, 0x68, 0x69, - 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2e, 0x69, 0x6e, 0x74, 0x65, - 0x72, 0x6e, 0x61, 0x6c, 0x2e, 0x70, 0x65, 0x65, 0x72, 0x69, 0x6e, 0x67, 0x2e, 0x50, 0x65, 0x65, - 0x72, 0x69, 0x6e, 0x67, 0x57, 0x72, 0x69, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x12, 0xa3, 0x01, 0x0a, 0x18, 0x54, 0x72, 0x75, 0x73, 0x74, 0x42, 0x75, 0x6e, 0x64, 0x6c, - 0x65, 0x4c, 0x69, 0x73, 0x74, 0x42, 0x79, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x42, - 0x2e, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x63, 0x6f, 0x6e, 0x73, 0x75, - 0x6c, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2e, 0x70, 0x65, 0x65, 0x72, 0x69, - 0x6e, 0x67, 0x2e, 0x54, 0x72, 0x75, 0x73, 0x74, 0x42, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x4c, 0x69, - 0x73, 0x74, 0x42, 0x79, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x43, 0x2e, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x63, - 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2e, 0x70, - 0x65, 0x65, 0x72, 0x69, 0x6e, 0x67, 0x2e, 0x54, 0x72, 0x75, 0x73, 0x74, 0x42, 0x75, 0x6e, 0x64, - 0x6c, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x42, 0x79, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x88, 0x01, 0x0a, 0x0f, 0x54, 0x72, 0x75, 0x73, - 0x74, 0x42, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x52, 0x65, 0x61, 0x64, 0x12, 0x39, 0x2e, 0x68, 0x61, + 0x6e, 0x67, 0x52, 0x65, 0x61, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x36, 0x2e, + 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6c, + 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2e, 0x70, 0x65, 0x65, 0x72, 0x69, 0x6e, + 0x67, 0x2e, 0x50, 0x65, 0x65, 0x72, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x61, 0x64, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x7c, 0x0a, 0x0b, 0x50, 0x65, 0x65, 0x72, 0x69, 0x6e, 0x67, + 0x4c, 0x69, 0x73, 0x74, 0x12, 0x35, 0x2e, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, + 0x2e, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, + 0x2e, 0x70, 0x65, 0x65, 0x72, 0x69, 0x6e, 0x67, 0x2e, 0x50, 0x65, 0x65, 0x72, 0x69, 0x6e, 0x67, + 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x36, 0x2e, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2e, 0x70, 0x65, 0x65, 0x72, 0x69, 0x6e, 0x67, 0x2e, - 0x54, 0x72, 0x75, 0x73, 0x74, 0x42, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x52, 0x65, 0x61, 0x64, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x3a, 0x2e, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, + 0x50, 0x65, 0x65, 0x72, 0x69, 0x6e, 0x67, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x82, 0x01, 0x0a, 0x0d, 0x50, 0x65, 0x65, 0x72, 0x69, 0x6e, 0x67, 0x44, + 0x65, 0x6c, 0x65, 0x74, 0x65, 0x12, 0x37, 0x2e, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, + 0x70, 0x2e, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, + 0x6c, 0x2e, 0x70, 0x65, 0x65, 0x72, 0x69, 0x6e, 0x67, 0x2e, 0x50, 0x65, 0x65, 0x72, 0x69, 0x6e, + 0x67, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x38, + 0x2e, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x63, 0x6f, 0x6e, 0x73, 0x75, + 0x6c, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2e, 0x70, 0x65, 0x65, 0x72, 0x69, + 0x6e, 0x67, 0x2e, 0x50, 0x65, 0x65, 0x72, 0x69, 0x6e, 0x67, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x7f, 0x0a, 0x0c, 0x50, 0x65, 0x65, 0x72, + 0x69, 0x6e, 0x67, 0x57, 0x72, 0x69, 0x74, 0x65, 0x12, 0x36, 0x2e, 0x68, 0x61, 0x73, 0x68, 0x69, + 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2e, 0x69, 0x6e, 0x74, 0x65, + 0x72, 0x6e, 0x61, 0x6c, 0x2e, 0x70, 0x65, 0x65, 0x72, 0x69, 0x6e, 0x67, 0x2e, 0x50, 0x65, 0x65, + 0x72, 0x69, 0x6e, 0x67, 0x57, 0x72, 0x69, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x37, 0x2e, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x63, 0x6f, 0x6e, + 0x73, 0x75, 0x6c, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2e, 0x70, 0x65, 0x65, + 0x72, 0x69, 0x6e, 0x67, 0x2e, 0x50, 0x65, 0x65, 0x72, 0x69, 0x6e, 0x67, 0x57, 0x72, 0x69, 0x74, + 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0xa3, 0x01, 0x0a, 0x18, 0x54, 0x72, + 0x75, 0x73, 0x74, 0x42, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x42, 0x79, 0x53, + 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x42, 0x2e, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2e, 0x70, 0x65, 0x65, 0x72, 0x69, 0x6e, 0x67, 0x2e, 0x54, 0x72, 0x75, 0x73, 0x74, - 0x42, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x52, 0x65, 0x61, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x42, 0x8a, 0x02, 0x0a, 0x25, 0x63, 0x6f, 0x6d, 0x2e, 0x68, 0x61, 0x73, 0x68, 0x69, - 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2e, 0x69, 0x6e, 0x74, 0x65, - 0x72, 0x6e, 0x61, 0x6c, 0x2e, 0x70, 0x65, 0x65, 0x72, 0x69, 0x6e, 0x67, 0x42, 0x0c, 0x50, 0x65, - 0x65, 0x72, 0x69, 0x6e, 0x67, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x2b, 0x67, 0x69, - 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, - 0x72, 0x70, 0x2f, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, - 0x70, 0x62, 0x70, 0x65, 0x65, 0x72, 0x69, 0x6e, 0x67, 0xa2, 0x02, 0x04, 0x48, 0x43, 0x49, 0x50, - 0xaa, 0x02, 0x21, 0x48, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x43, 0x6f, 0x6e, - 0x73, 0x75, 0x6c, 0x2e, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2e, 0x50, 0x65, 0x65, - 0x72, 0x69, 0x6e, 0x67, 0xca, 0x02, 0x21, 0x48, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, - 0x5c, 0x43, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x5c, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, - 0x5c, 0x50, 0x65, 0x65, 0x72, 0x69, 0x6e, 0x67, 0xe2, 0x02, 0x2d, 0x48, 0x61, 0x73, 0x68, 0x69, - 0x63, 0x6f, 0x72, 0x70, 0x5c, 0x43, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x5c, 0x49, 0x6e, 0x74, 0x65, - 0x72, 0x6e, 0x61, 0x6c, 0x5c, 0x50, 0x65, 0x65, 0x72, 0x69, 0x6e, 0x67, 0x5c, 0x47, 0x50, 0x42, - 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x24, 0x48, 0x61, 0x73, 0x68, 0x69, - 0x63, 0x6f, 0x72, 0x70, 0x3a, 0x3a, 0x43, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x3a, 0x3a, 0x49, 0x6e, - 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x3a, 0x3a, 0x50, 0x65, 0x65, 0x72, 0x69, 0x6e, 0x67, 0x62, - 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x42, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x42, 0x79, 0x53, 0x65, 0x72, 0x76, + 0x69, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x43, 0x2e, 0x68, 0x61, 0x73, + 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2e, 0x69, 0x6e, + 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2e, 0x70, 0x65, 0x65, 0x72, 0x69, 0x6e, 0x67, 0x2e, 0x54, + 0x72, 0x75, 0x73, 0x74, 0x42, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x42, 0x79, + 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, + 0x88, 0x01, 0x0a, 0x0f, 0x54, 0x72, 0x75, 0x73, 0x74, 0x42, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x52, + 0x65, 0x61, 0x64, 0x12, 0x39, 0x2e, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2e, + 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2e, + 0x70, 0x65, 0x65, 0x72, 0x69, 0x6e, 0x67, 0x2e, 0x54, 0x72, 0x75, 0x73, 0x74, 0x42, 0x75, 0x6e, + 0x64, 0x6c, 0x65, 0x52, 0x65, 0x61, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x3a, + 0x2e, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x63, 0x6f, 0x6e, 0x73, 0x75, + 0x6c, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2e, 0x70, 0x65, 0x65, 0x72, 0x69, + 0x6e, 0x67, 0x2e, 0x54, 0x72, 0x75, 0x73, 0x74, 0x42, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x52, 0x65, + 0x61, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x8a, 0x02, 0x0a, 0x25, 0x63, + 0x6f, 0x6d, 0x2e, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x63, 0x6f, 0x6e, + 0x73, 0x75, 0x6c, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2e, 0x70, 0x65, 0x65, + 0x72, 0x69, 0x6e, 0x67, 0x42, 0x0c, 0x50, 0x65, 0x65, 0x72, 0x69, 0x6e, 0x67, 0x50, 0x72, 0x6f, + 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x2b, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, + 0x2f, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2f, 0x63, 0x6f, 0x6e, 0x73, 0x75, + 0x6c, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x70, 0x62, 0x70, 0x65, 0x65, 0x72, 0x69, 0x6e, + 0x67, 0xa2, 0x02, 0x04, 0x48, 0x43, 0x49, 0x50, 0xaa, 0x02, 0x21, 0x48, 0x61, 0x73, 0x68, 0x69, + 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x43, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2e, 0x49, 0x6e, 0x74, 0x65, + 0x72, 0x6e, 0x61, 0x6c, 0x2e, 0x50, 0x65, 0x65, 0x72, 0x69, 0x6e, 0x67, 0xca, 0x02, 0x21, 0x48, + 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x5c, 0x43, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x5c, + 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x5c, 0x50, 0x65, 0x65, 0x72, 0x69, 0x6e, 0x67, + 0xe2, 0x02, 0x2d, 0x48, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x5c, 0x43, 0x6f, 0x6e, + 0x73, 0x75, 0x6c, 0x5c, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x5c, 0x50, 0x65, 0x65, + 0x72, 0x69, 0x6e, 0x67, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, + 0xea, 0x02, 0x24, 0x48, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x3a, 0x3a, 0x43, 0x6f, + 0x6e, 0x73, 0x75, 0x6c, 0x3a, 0x3a, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x3a, 0x3a, + 0x50, 0x65, 0x65, 0x72, 0x69, 0x6e, 0x67, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -2447,83 +2499,84 @@ func file_proto_pbpeering_peering_proto_rawDescGZIP() []byte { } var file_proto_pbpeering_peering_proto_enumTypes = make([]protoimpl.EnumInfo, 1) -var file_proto_pbpeering_peering_proto_msgTypes = make([]protoimpl.MessageInfo, 36) +var file_proto_pbpeering_peering_proto_msgTypes = make([]protoimpl.MessageInfo, 37) var file_proto_pbpeering_peering_proto_goTypes = []interface{}{ (PeeringState)(0), // 0: hashicorp.consul.internal.peering.PeeringState (*SecretsWriteRequest)(nil), // 1: hashicorp.consul.internal.peering.SecretsWriteRequest (*PeeringSecrets)(nil), // 2: hashicorp.consul.internal.peering.PeeringSecrets (*Peering)(nil), // 3: hashicorp.consul.internal.peering.Peering (*PeeringTrustBundle)(nil), // 4: hashicorp.consul.internal.peering.PeeringTrustBundle - (*PeeringReadRequest)(nil), // 5: hashicorp.consul.internal.peering.PeeringReadRequest - (*PeeringReadResponse)(nil), // 6: hashicorp.consul.internal.peering.PeeringReadResponse - (*PeeringListRequest)(nil), // 7: hashicorp.consul.internal.peering.PeeringListRequest - (*PeeringListResponse)(nil), // 8: hashicorp.consul.internal.peering.PeeringListResponse - (*PeeringWriteRequest)(nil), // 9: hashicorp.consul.internal.peering.PeeringWriteRequest - (*PeeringWriteResponse)(nil), // 10: hashicorp.consul.internal.peering.PeeringWriteResponse - (*PeeringDeleteRequest)(nil), // 11: hashicorp.consul.internal.peering.PeeringDeleteRequest - (*PeeringDeleteResponse)(nil), // 12: hashicorp.consul.internal.peering.PeeringDeleteResponse - (*TrustBundleListByServiceRequest)(nil), // 13: hashicorp.consul.internal.peering.TrustBundleListByServiceRequest - (*TrustBundleListByServiceResponse)(nil), // 14: hashicorp.consul.internal.peering.TrustBundleListByServiceResponse - (*TrustBundleReadRequest)(nil), // 15: hashicorp.consul.internal.peering.TrustBundleReadRequest - (*TrustBundleReadResponse)(nil), // 16: hashicorp.consul.internal.peering.TrustBundleReadResponse - (*PeeringTerminateByIDRequest)(nil), // 17: hashicorp.consul.internal.peering.PeeringTerminateByIDRequest - (*PeeringTerminateByIDResponse)(nil), // 18: hashicorp.consul.internal.peering.PeeringTerminateByIDResponse - (*PeeringTrustBundleWriteRequest)(nil), // 19: hashicorp.consul.internal.peering.PeeringTrustBundleWriteRequest - (*PeeringTrustBundleWriteResponse)(nil), // 20: hashicorp.consul.internal.peering.PeeringTrustBundleWriteResponse - (*PeeringTrustBundleDeleteRequest)(nil), // 21: hashicorp.consul.internal.peering.PeeringTrustBundleDeleteRequest - (*PeeringTrustBundleDeleteResponse)(nil), // 22: hashicorp.consul.internal.peering.PeeringTrustBundleDeleteResponse - (*GenerateTokenRequest)(nil), // 23: hashicorp.consul.internal.peering.GenerateTokenRequest - (*GenerateTokenResponse)(nil), // 24: hashicorp.consul.internal.peering.GenerateTokenResponse - (*EstablishRequest)(nil), // 25: hashicorp.consul.internal.peering.EstablishRequest - (*EstablishResponse)(nil), // 26: hashicorp.consul.internal.peering.EstablishResponse - (*SecretsWriteRequest_GenerateTokenRequest)(nil), // 27: hashicorp.consul.internal.peering.SecretsWriteRequest.GenerateTokenRequest - (*SecretsWriteRequest_ExchangeSecretRequest)(nil), // 28: hashicorp.consul.internal.peering.SecretsWriteRequest.ExchangeSecretRequest - (*SecretsWriteRequest_PromotePendingRequest)(nil), // 29: hashicorp.consul.internal.peering.SecretsWriteRequest.PromotePendingRequest - (*SecretsWriteRequest_EstablishRequest)(nil), // 30: hashicorp.consul.internal.peering.SecretsWriteRequest.EstablishRequest - (*PeeringSecrets_Establishment)(nil), // 31: hashicorp.consul.internal.peering.PeeringSecrets.Establishment - (*PeeringSecrets_Stream)(nil), // 32: hashicorp.consul.internal.peering.PeeringSecrets.Stream - nil, // 33: hashicorp.consul.internal.peering.Peering.MetaEntry - nil, // 34: hashicorp.consul.internal.peering.PeeringWriteRequest.MetaEntry - nil, // 35: hashicorp.consul.internal.peering.GenerateTokenRequest.MetaEntry - nil, // 36: hashicorp.consul.internal.peering.EstablishRequest.MetaEntry - (*timestamppb.Timestamp)(nil), // 37: google.protobuf.Timestamp + (*PeeringServerAddresses)(nil), // 5: hashicorp.consul.internal.peering.PeeringServerAddresses + (*PeeringReadRequest)(nil), // 6: hashicorp.consul.internal.peering.PeeringReadRequest + (*PeeringReadResponse)(nil), // 7: hashicorp.consul.internal.peering.PeeringReadResponse + (*PeeringListRequest)(nil), // 8: hashicorp.consul.internal.peering.PeeringListRequest + (*PeeringListResponse)(nil), // 9: hashicorp.consul.internal.peering.PeeringListResponse + (*PeeringWriteRequest)(nil), // 10: hashicorp.consul.internal.peering.PeeringWriteRequest + (*PeeringWriteResponse)(nil), // 11: hashicorp.consul.internal.peering.PeeringWriteResponse + (*PeeringDeleteRequest)(nil), // 12: hashicorp.consul.internal.peering.PeeringDeleteRequest + (*PeeringDeleteResponse)(nil), // 13: hashicorp.consul.internal.peering.PeeringDeleteResponse + (*TrustBundleListByServiceRequest)(nil), // 14: hashicorp.consul.internal.peering.TrustBundleListByServiceRequest + (*TrustBundleListByServiceResponse)(nil), // 15: hashicorp.consul.internal.peering.TrustBundleListByServiceResponse + (*TrustBundleReadRequest)(nil), // 16: hashicorp.consul.internal.peering.TrustBundleReadRequest + (*TrustBundleReadResponse)(nil), // 17: hashicorp.consul.internal.peering.TrustBundleReadResponse + (*PeeringTerminateByIDRequest)(nil), // 18: hashicorp.consul.internal.peering.PeeringTerminateByIDRequest + (*PeeringTerminateByIDResponse)(nil), // 19: hashicorp.consul.internal.peering.PeeringTerminateByIDResponse + (*PeeringTrustBundleWriteRequest)(nil), // 20: hashicorp.consul.internal.peering.PeeringTrustBundleWriteRequest + (*PeeringTrustBundleWriteResponse)(nil), // 21: hashicorp.consul.internal.peering.PeeringTrustBundleWriteResponse + (*PeeringTrustBundleDeleteRequest)(nil), // 22: hashicorp.consul.internal.peering.PeeringTrustBundleDeleteRequest + (*PeeringTrustBundleDeleteResponse)(nil), // 23: hashicorp.consul.internal.peering.PeeringTrustBundleDeleteResponse + (*GenerateTokenRequest)(nil), // 24: hashicorp.consul.internal.peering.GenerateTokenRequest + (*GenerateTokenResponse)(nil), // 25: hashicorp.consul.internal.peering.GenerateTokenResponse + (*EstablishRequest)(nil), // 26: hashicorp.consul.internal.peering.EstablishRequest + (*EstablishResponse)(nil), // 27: hashicorp.consul.internal.peering.EstablishResponse + (*SecretsWriteRequest_GenerateTokenRequest)(nil), // 28: hashicorp.consul.internal.peering.SecretsWriteRequest.GenerateTokenRequest + (*SecretsWriteRequest_ExchangeSecretRequest)(nil), // 29: hashicorp.consul.internal.peering.SecretsWriteRequest.ExchangeSecretRequest + (*SecretsWriteRequest_PromotePendingRequest)(nil), // 30: hashicorp.consul.internal.peering.SecretsWriteRequest.PromotePendingRequest + (*SecretsWriteRequest_EstablishRequest)(nil), // 31: hashicorp.consul.internal.peering.SecretsWriteRequest.EstablishRequest + (*PeeringSecrets_Establishment)(nil), // 32: hashicorp.consul.internal.peering.PeeringSecrets.Establishment + (*PeeringSecrets_Stream)(nil), // 33: hashicorp.consul.internal.peering.PeeringSecrets.Stream + nil, // 34: hashicorp.consul.internal.peering.Peering.MetaEntry + nil, // 35: hashicorp.consul.internal.peering.PeeringWriteRequest.MetaEntry + nil, // 36: hashicorp.consul.internal.peering.GenerateTokenRequest.MetaEntry + nil, // 37: hashicorp.consul.internal.peering.EstablishRequest.MetaEntry + (*timestamppb.Timestamp)(nil), // 38: google.protobuf.Timestamp } var file_proto_pbpeering_peering_proto_depIdxs = []int32{ - 27, // 0: hashicorp.consul.internal.peering.SecretsWriteRequest.generate_token:type_name -> hashicorp.consul.internal.peering.SecretsWriteRequest.GenerateTokenRequest - 28, // 1: hashicorp.consul.internal.peering.SecretsWriteRequest.exchange_secret:type_name -> hashicorp.consul.internal.peering.SecretsWriteRequest.ExchangeSecretRequest - 29, // 2: hashicorp.consul.internal.peering.SecretsWriteRequest.promote_pending:type_name -> hashicorp.consul.internal.peering.SecretsWriteRequest.PromotePendingRequest - 30, // 3: hashicorp.consul.internal.peering.SecretsWriteRequest.establish:type_name -> hashicorp.consul.internal.peering.SecretsWriteRequest.EstablishRequest - 31, // 4: hashicorp.consul.internal.peering.PeeringSecrets.establishment:type_name -> hashicorp.consul.internal.peering.PeeringSecrets.Establishment - 32, // 5: hashicorp.consul.internal.peering.PeeringSecrets.stream:type_name -> hashicorp.consul.internal.peering.PeeringSecrets.Stream - 37, // 6: hashicorp.consul.internal.peering.Peering.DeletedAt:type_name -> google.protobuf.Timestamp - 33, // 7: hashicorp.consul.internal.peering.Peering.Meta:type_name -> hashicorp.consul.internal.peering.Peering.MetaEntry + 28, // 0: hashicorp.consul.internal.peering.SecretsWriteRequest.generate_token:type_name -> hashicorp.consul.internal.peering.SecretsWriteRequest.GenerateTokenRequest + 29, // 1: hashicorp.consul.internal.peering.SecretsWriteRequest.exchange_secret:type_name -> hashicorp.consul.internal.peering.SecretsWriteRequest.ExchangeSecretRequest + 30, // 2: hashicorp.consul.internal.peering.SecretsWriteRequest.promote_pending:type_name -> hashicorp.consul.internal.peering.SecretsWriteRequest.PromotePendingRequest + 31, // 3: hashicorp.consul.internal.peering.SecretsWriteRequest.establish:type_name -> hashicorp.consul.internal.peering.SecretsWriteRequest.EstablishRequest + 32, // 4: hashicorp.consul.internal.peering.PeeringSecrets.establishment:type_name -> hashicorp.consul.internal.peering.PeeringSecrets.Establishment + 33, // 5: hashicorp.consul.internal.peering.PeeringSecrets.stream:type_name -> hashicorp.consul.internal.peering.PeeringSecrets.Stream + 38, // 6: hashicorp.consul.internal.peering.Peering.DeletedAt:type_name -> google.protobuf.Timestamp + 34, // 7: hashicorp.consul.internal.peering.Peering.Meta:type_name -> hashicorp.consul.internal.peering.Peering.MetaEntry 0, // 8: hashicorp.consul.internal.peering.Peering.State:type_name -> hashicorp.consul.internal.peering.PeeringState 3, // 9: hashicorp.consul.internal.peering.PeeringReadResponse.Peering:type_name -> hashicorp.consul.internal.peering.Peering 3, // 10: hashicorp.consul.internal.peering.PeeringListResponse.Peerings:type_name -> hashicorp.consul.internal.peering.Peering 3, // 11: hashicorp.consul.internal.peering.PeeringWriteRequest.Peering:type_name -> hashicorp.consul.internal.peering.Peering 1, // 12: hashicorp.consul.internal.peering.PeeringWriteRequest.SecretsRequest:type_name -> hashicorp.consul.internal.peering.SecretsWriteRequest - 34, // 13: hashicorp.consul.internal.peering.PeeringWriteRequest.Meta:type_name -> hashicorp.consul.internal.peering.PeeringWriteRequest.MetaEntry + 35, // 13: hashicorp.consul.internal.peering.PeeringWriteRequest.Meta:type_name -> hashicorp.consul.internal.peering.PeeringWriteRequest.MetaEntry 4, // 14: hashicorp.consul.internal.peering.TrustBundleListByServiceResponse.Bundles:type_name -> hashicorp.consul.internal.peering.PeeringTrustBundle 4, // 15: hashicorp.consul.internal.peering.TrustBundleReadResponse.Bundle:type_name -> hashicorp.consul.internal.peering.PeeringTrustBundle 4, // 16: hashicorp.consul.internal.peering.PeeringTrustBundleWriteRequest.PeeringTrustBundle:type_name -> hashicorp.consul.internal.peering.PeeringTrustBundle - 35, // 17: hashicorp.consul.internal.peering.GenerateTokenRequest.Meta:type_name -> hashicorp.consul.internal.peering.GenerateTokenRequest.MetaEntry - 36, // 18: hashicorp.consul.internal.peering.EstablishRequest.Meta:type_name -> hashicorp.consul.internal.peering.EstablishRequest.MetaEntry - 23, // 19: hashicorp.consul.internal.peering.PeeringService.GenerateToken:input_type -> hashicorp.consul.internal.peering.GenerateTokenRequest - 25, // 20: hashicorp.consul.internal.peering.PeeringService.Establish:input_type -> hashicorp.consul.internal.peering.EstablishRequest - 5, // 21: hashicorp.consul.internal.peering.PeeringService.PeeringRead:input_type -> hashicorp.consul.internal.peering.PeeringReadRequest - 7, // 22: hashicorp.consul.internal.peering.PeeringService.PeeringList:input_type -> hashicorp.consul.internal.peering.PeeringListRequest - 11, // 23: hashicorp.consul.internal.peering.PeeringService.PeeringDelete:input_type -> hashicorp.consul.internal.peering.PeeringDeleteRequest - 9, // 24: hashicorp.consul.internal.peering.PeeringService.PeeringWrite:input_type -> hashicorp.consul.internal.peering.PeeringWriteRequest - 13, // 25: hashicorp.consul.internal.peering.PeeringService.TrustBundleListByService:input_type -> hashicorp.consul.internal.peering.TrustBundleListByServiceRequest - 15, // 26: hashicorp.consul.internal.peering.PeeringService.TrustBundleRead:input_type -> hashicorp.consul.internal.peering.TrustBundleReadRequest - 24, // 27: hashicorp.consul.internal.peering.PeeringService.GenerateToken:output_type -> hashicorp.consul.internal.peering.GenerateTokenResponse - 26, // 28: hashicorp.consul.internal.peering.PeeringService.Establish:output_type -> hashicorp.consul.internal.peering.EstablishResponse - 6, // 29: hashicorp.consul.internal.peering.PeeringService.PeeringRead:output_type -> hashicorp.consul.internal.peering.PeeringReadResponse - 8, // 30: hashicorp.consul.internal.peering.PeeringService.PeeringList:output_type -> hashicorp.consul.internal.peering.PeeringListResponse - 12, // 31: hashicorp.consul.internal.peering.PeeringService.PeeringDelete:output_type -> hashicorp.consul.internal.peering.PeeringDeleteResponse - 10, // 32: hashicorp.consul.internal.peering.PeeringService.PeeringWrite:output_type -> hashicorp.consul.internal.peering.PeeringWriteResponse - 14, // 33: hashicorp.consul.internal.peering.PeeringService.TrustBundleListByService:output_type -> hashicorp.consul.internal.peering.TrustBundleListByServiceResponse - 16, // 34: hashicorp.consul.internal.peering.PeeringService.TrustBundleRead:output_type -> hashicorp.consul.internal.peering.TrustBundleReadResponse + 36, // 17: hashicorp.consul.internal.peering.GenerateTokenRequest.Meta:type_name -> hashicorp.consul.internal.peering.GenerateTokenRequest.MetaEntry + 37, // 18: hashicorp.consul.internal.peering.EstablishRequest.Meta:type_name -> hashicorp.consul.internal.peering.EstablishRequest.MetaEntry + 24, // 19: hashicorp.consul.internal.peering.PeeringService.GenerateToken:input_type -> hashicorp.consul.internal.peering.GenerateTokenRequest + 26, // 20: hashicorp.consul.internal.peering.PeeringService.Establish:input_type -> hashicorp.consul.internal.peering.EstablishRequest + 6, // 21: hashicorp.consul.internal.peering.PeeringService.PeeringRead:input_type -> hashicorp.consul.internal.peering.PeeringReadRequest + 8, // 22: hashicorp.consul.internal.peering.PeeringService.PeeringList:input_type -> hashicorp.consul.internal.peering.PeeringListRequest + 12, // 23: hashicorp.consul.internal.peering.PeeringService.PeeringDelete:input_type -> hashicorp.consul.internal.peering.PeeringDeleteRequest + 10, // 24: hashicorp.consul.internal.peering.PeeringService.PeeringWrite:input_type -> hashicorp.consul.internal.peering.PeeringWriteRequest + 14, // 25: hashicorp.consul.internal.peering.PeeringService.TrustBundleListByService:input_type -> hashicorp.consul.internal.peering.TrustBundleListByServiceRequest + 16, // 26: hashicorp.consul.internal.peering.PeeringService.TrustBundleRead:input_type -> hashicorp.consul.internal.peering.TrustBundleReadRequest + 25, // 27: hashicorp.consul.internal.peering.PeeringService.GenerateToken:output_type -> hashicorp.consul.internal.peering.GenerateTokenResponse + 27, // 28: hashicorp.consul.internal.peering.PeeringService.Establish:output_type -> hashicorp.consul.internal.peering.EstablishResponse + 7, // 29: hashicorp.consul.internal.peering.PeeringService.PeeringRead:output_type -> hashicorp.consul.internal.peering.PeeringReadResponse + 9, // 30: hashicorp.consul.internal.peering.PeeringService.PeeringList:output_type -> hashicorp.consul.internal.peering.PeeringListResponse + 13, // 31: hashicorp.consul.internal.peering.PeeringService.PeeringDelete:output_type -> hashicorp.consul.internal.peering.PeeringDeleteResponse + 11, // 32: hashicorp.consul.internal.peering.PeeringService.PeeringWrite:output_type -> hashicorp.consul.internal.peering.PeeringWriteResponse + 15, // 33: hashicorp.consul.internal.peering.PeeringService.TrustBundleListByService:output_type -> hashicorp.consul.internal.peering.TrustBundleListByServiceResponse + 17, // 34: hashicorp.consul.internal.peering.PeeringService.TrustBundleRead:output_type -> hashicorp.consul.internal.peering.TrustBundleReadResponse 27, // [27:35] is the sub-list for method output_type 19, // [19:27] is the sub-list for method input_type 19, // [19:19] is the sub-list for extension type_name @@ -2586,7 +2639,7 @@ func file_proto_pbpeering_peering_proto_init() { } } file_proto_pbpeering_peering_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*PeeringReadRequest); i { + switch v := v.(*PeeringServerAddresses); i { case 0: return &v.state case 1: @@ -2598,7 +2651,7 @@ func file_proto_pbpeering_peering_proto_init() { } } file_proto_pbpeering_peering_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*PeeringReadResponse); i { + switch v := v.(*PeeringReadRequest); i { case 0: return &v.state case 1: @@ -2610,7 +2663,7 @@ func file_proto_pbpeering_peering_proto_init() { } } file_proto_pbpeering_peering_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*PeeringListRequest); i { + switch v := v.(*PeeringReadResponse); i { case 0: return &v.state case 1: @@ -2622,7 +2675,7 @@ func file_proto_pbpeering_peering_proto_init() { } } file_proto_pbpeering_peering_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*PeeringListResponse); i { + switch v := v.(*PeeringListRequest); i { case 0: return &v.state case 1: @@ -2634,7 +2687,7 @@ func file_proto_pbpeering_peering_proto_init() { } } file_proto_pbpeering_peering_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*PeeringWriteRequest); i { + switch v := v.(*PeeringListResponse); i { case 0: return &v.state case 1: @@ -2646,7 +2699,7 @@ func file_proto_pbpeering_peering_proto_init() { } } file_proto_pbpeering_peering_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*PeeringWriteResponse); i { + switch v := v.(*PeeringWriteRequest); i { case 0: return &v.state case 1: @@ -2658,7 +2711,7 @@ func file_proto_pbpeering_peering_proto_init() { } } file_proto_pbpeering_peering_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*PeeringDeleteRequest); i { + switch v := v.(*PeeringWriteResponse); i { case 0: return &v.state case 1: @@ -2670,7 +2723,7 @@ func file_proto_pbpeering_peering_proto_init() { } } file_proto_pbpeering_peering_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*PeeringDeleteResponse); i { + switch v := v.(*PeeringDeleteRequest); i { case 0: return &v.state case 1: @@ -2682,7 +2735,7 @@ func file_proto_pbpeering_peering_proto_init() { } } file_proto_pbpeering_peering_proto_msgTypes[12].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*TrustBundleListByServiceRequest); i { + switch v := v.(*PeeringDeleteResponse); i { case 0: return &v.state case 1: @@ -2694,7 +2747,7 @@ func file_proto_pbpeering_peering_proto_init() { } } file_proto_pbpeering_peering_proto_msgTypes[13].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*TrustBundleListByServiceResponse); i { + switch v := v.(*TrustBundleListByServiceRequest); i { case 0: return &v.state case 1: @@ -2706,7 +2759,7 @@ func file_proto_pbpeering_peering_proto_init() { } } file_proto_pbpeering_peering_proto_msgTypes[14].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*TrustBundleReadRequest); i { + switch v := v.(*TrustBundleListByServiceResponse); i { case 0: return &v.state case 1: @@ -2718,7 +2771,7 @@ func file_proto_pbpeering_peering_proto_init() { } } file_proto_pbpeering_peering_proto_msgTypes[15].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*TrustBundleReadResponse); i { + switch v := v.(*TrustBundleReadRequest); i { case 0: return &v.state case 1: @@ -2730,7 +2783,7 @@ func file_proto_pbpeering_peering_proto_init() { } } file_proto_pbpeering_peering_proto_msgTypes[16].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*PeeringTerminateByIDRequest); i { + switch v := v.(*TrustBundleReadResponse); i { case 0: return &v.state case 1: @@ -2742,7 +2795,7 @@ func file_proto_pbpeering_peering_proto_init() { } } file_proto_pbpeering_peering_proto_msgTypes[17].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*PeeringTerminateByIDResponse); i { + switch v := v.(*PeeringTerminateByIDRequest); i { case 0: return &v.state case 1: @@ -2754,7 +2807,7 @@ func file_proto_pbpeering_peering_proto_init() { } } file_proto_pbpeering_peering_proto_msgTypes[18].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*PeeringTrustBundleWriteRequest); i { + switch v := v.(*PeeringTerminateByIDResponse); i { case 0: return &v.state case 1: @@ -2766,7 +2819,7 @@ func file_proto_pbpeering_peering_proto_init() { } } file_proto_pbpeering_peering_proto_msgTypes[19].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*PeeringTrustBundleWriteResponse); i { + switch v := v.(*PeeringTrustBundleWriteRequest); i { case 0: return &v.state case 1: @@ -2778,7 +2831,7 @@ func file_proto_pbpeering_peering_proto_init() { } } file_proto_pbpeering_peering_proto_msgTypes[20].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*PeeringTrustBundleDeleteRequest); i { + switch v := v.(*PeeringTrustBundleWriteResponse); i { case 0: return &v.state case 1: @@ -2790,7 +2843,7 @@ func file_proto_pbpeering_peering_proto_init() { } } file_proto_pbpeering_peering_proto_msgTypes[21].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*PeeringTrustBundleDeleteResponse); i { + switch v := v.(*PeeringTrustBundleDeleteRequest); i { case 0: return &v.state case 1: @@ -2802,7 +2855,7 @@ func file_proto_pbpeering_peering_proto_init() { } } file_proto_pbpeering_peering_proto_msgTypes[22].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GenerateTokenRequest); i { + switch v := v.(*PeeringTrustBundleDeleteResponse); i { case 0: return &v.state case 1: @@ -2814,7 +2867,7 @@ func file_proto_pbpeering_peering_proto_init() { } } file_proto_pbpeering_peering_proto_msgTypes[23].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GenerateTokenResponse); i { + switch v := v.(*GenerateTokenRequest); i { case 0: return &v.state case 1: @@ -2826,7 +2879,7 @@ func file_proto_pbpeering_peering_proto_init() { } } file_proto_pbpeering_peering_proto_msgTypes[24].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*EstablishRequest); i { + switch v := v.(*GenerateTokenResponse); i { case 0: return &v.state case 1: @@ -2838,7 +2891,7 @@ func file_proto_pbpeering_peering_proto_init() { } } file_proto_pbpeering_peering_proto_msgTypes[25].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*EstablishResponse); i { + switch v := v.(*EstablishRequest); i { case 0: return &v.state case 1: @@ -2850,7 +2903,7 @@ func file_proto_pbpeering_peering_proto_init() { } } file_proto_pbpeering_peering_proto_msgTypes[26].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*SecretsWriteRequest_GenerateTokenRequest); i { + switch v := v.(*EstablishResponse); i { case 0: return &v.state case 1: @@ -2862,7 +2915,7 @@ func file_proto_pbpeering_peering_proto_init() { } } file_proto_pbpeering_peering_proto_msgTypes[27].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*SecretsWriteRequest_ExchangeSecretRequest); i { + switch v := v.(*SecretsWriteRequest_GenerateTokenRequest); i { case 0: return &v.state case 1: @@ -2874,7 +2927,7 @@ func file_proto_pbpeering_peering_proto_init() { } } file_proto_pbpeering_peering_proto_msgTypes[28].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*SecretsWriteRequest_PromotePendingRequest); i { + switch v := v.(*SecretsWriteRequest_ExchangeSecretRequest); i { case 0: return &v.state case 1: @@ -2886,7 +2939,7 @@ func file_proto_pbpeering_peering_proto_init() { } } file_proto_pbpeering_peering_proto_msgTypes[29].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*SecretsWriteRequest_EstablishRequest); i { + switch v := v.(*SecretsWriteRequest_PromotePendingRequest); i { case 0: return &v.state case 1: @@ -2898,7 +2951,7 @@ func file_proto_pbpeering_peering_proto_init() { } } file_proto_pbpeering_peering_proto_msgTypes[30].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*PeeringSecrets_Establishment); i { + switch v := v.(*SecretsWriteRequest_EstablishRequest); i { case 0: return &v.state case 1: @@ -2910,6 +2963,18 @@ func file_proto_pbpeering_peering_proto_init() { } } file_proto_pbpeering_peering_proto_msgTypes[31].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*PeeringSecrets_Establishment); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_proto_pbpeering_peering_proto_msgTypes[32].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*PeeringSecrets_Stream); i { case 0: return &v.state @@ -2934,7 +2999,7 @@ func file_proto_pbpeering_peering_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_proto_pbpeering_peering_proto_rawDesc, NumEnums: 1, - NumMessages: 36, + NumMessages: 37, NumExtensions: 0, NumServices: 1, }, diff --git a/proto/pbpeering/peering.proto b/proto/pbpeering/peering.proto index 4283c3c32..cc37c6041 100644 --- a/proto/pbpeering/peering.proto +++ b/proto/pbpeering/peering.proto @@ -225,6 +225,12 @@ message PeeringTrustBundle { uint64 ModifyIndex = 7; } +// PeeringServerAddresses contains the latest snapshot of all known +// server addresses for a peer. +message PeeringServerAddresses { + repeated string Addresses = 1; +} + // @consul-rpc-glue: LeaderReadTODO message PeeringReadRequest { string Name = 1; diff --git a/proto/pbpeerstream/types.go b/proto/pbpeerstream/types.go index df300cccd..4bf114c0e 100644 --- a/proto/pbpeerstream/types.go +++ b/proto/pbpeerstream/types.go @@ -3,13 +3,14 @@ package pbpeerstream const ( apiTypePrefix = "type.googleapis.com/" - TypeURLExportedService = apiTypePrefix + "hashicorp.consul.internal.peerstream.ExportedService" - TypeURLPeeringTrustBundle = apiTypePrefix + "hashicorp.consul.internal.peering.PeeringTrustBundle" + TypeURLExportedService = apiTypePrefix + "hashicorp.consul.internal.peerstream.ExportedService" + TypeURLPeeringTrustBundle = apiTypePrefix + "hashicorp.consul.internal.peering.PeeringTrustBundle" + TypeURLPeeringServerAddresses = apiTypePrefix + "hashicorp.consul.internal.peering.PeeringServerAddresses" ) func KnownTypeURL(s string) bool { switch s { - case TypeURLExportedService, TypeURLPeeringTrustBundle: + case TypeURLExportedService, TypeURLPeeringTrustBundle, TypeURLPeeringServerAddresses: return true } return false diff --git a/ui/packages/consul-hcp/app/components/consul/hcp/home/index.hbs b/ui/packages/consul-hcp/app/components/consul/hcp/home/index.hbs index 053f235da..1ab14457d 100644 --- a/ui/packages/consul-hcp/app/components/consul/hcp/home/index.hbs +++ b/ui/packages/consul-hcp/app/components/consul/hcp/home/index.hbs @@ -2,7 +2,7 @@ class="consul-hcp-home" ...attributes > - + Back to HCP diff --git a/ui/packages/consul-hcp/app/components/consul/hcp/home/index.test.js b/ui/packages/consul-hcp/app/components/consul/hcp/home/index.test.js new file mode 100644 index 000000000..84779d825 --- /dev/null +++ b/ui/packages/consul-hcp/app/components/consul/hcp/home/index.test.js @@ -0,0 +1,38 @@ +import { module, test } from 'qunit'; +import { setupRenderingTest } from 'ember-qunit'; +import { render } from '@ember/test-helpers'; +import hbs from 'htmlbars-inline-precompile'; + +import ConsulHcpHome from 'consul-ui/components/consul/hcp/home'; + +module('Integration | Component | consul hcp home', function(hooks) { + setupRenderingTest(hooks); + + test('it prints the value of CONSUL_HCP_URL', async function(assert) { + // temporary registration until we are running as separate applications + this.owner.register( + 'component:consul/hcp/home', + ConsulHcpHome + ); + // + + const Helper = this.owner.resolveRegistration('helper:env'); + this.owner.register( + 'helper:env', + class extends Helper { + compute([name, def]) { + switch(name) { + case 'CONSUL_HCP_URL': + return 'http://hcp'; + } + return super.compute(...arguments); + } + } + ); + + await render(hbs``); + + assert.dom('a').hasAttribute('href', 'http://hcp'); + + }); +}); diff --git a/ui/packages/consul-ui/app/components/consul/datacenter/selector/index.hbs b/ui/packages/consul-ui/app/components/consul/datacenter/selector/index.hbs index be1c33da4..72b44e3a9 100644 --- a/ui/packages/consul-ui/app/components/consul/datacenter/selector/index.hbs +++ b/ui/packages/consul-ui/app/components/consul/datacenter/selector/index.hbs @@ -58,6 +58,9 @@ {{else}}
{{@dcs.firstObject.Name}} + {{#if (env 'CONSUL_HCP_MANAGED_RUNTIME')}} + Self-managed + {{/if}}
{{/if}} diff --git a/ui/packages/consul-ui/app/components/hashicorp-consul/index.scss b/ui/packages/consul-ui/app/components/hashicorp-consul/index.scss index e33168eaa..fe989b06c 100644 --- a/ui/packages/consul-ui/app/components/hashicorp-consul/index.scss +++ b/ui/packages/consul-ui/app/components/hashicorp-consul/index.scss @@ -13,6 +13,17 @@ nav .dcs li.is-local span { @extend %menu-panel-badge; } + nav .dcs .dc-name { + color: rgb(var(--tone-gray-600)); + padding: 3.25px 0px; + font-weight: var(--typo-weight-semibold) + } + nav .dcs .dc-name span { + @extend %pill-200; + margin-left: 1rem; + background-color: rgb(var(--tone-gray-300)); + color: rgb(var(--tone-gray-999)); + } nav li.partitions, nav li.nspaces { @extend %main-nav-vertical-popover-menu; @@ -50,11 +61,6 @@ top: 2px; margin-left: 2px; } - .dc-name { - color: rgb(var(--tone-gray-600)); - padding: 3.25px 0px; - font-weight: var(--typo-weight-semibold) - } } .hashicorp-consul { @extend %hashicorp-consul; diff --git a/ui/packages/consul-ui/app/components/tab-nav/pageobject.js b/ui/packages/consul-ui/app/components/tab-nav/pageobject.js index 19d371f3b..2ec50e6a8 100644 --- a/ui/packages/consul-ui/app/components/tab-nav/pageobject.js +++ b/ui/packages/consul-ui/app/components/tab-nav/pageobject.js @@ -1,4 +1,4 @@ -import { is, clickable, attribute } from 'ember-cli-page-object'; +import { is, clickable, attribute, isVisible } from 'ember-cli-page-object'; import ucfirst from 'consul-ui/utils/ucfirst'; export default function(name, items, blankKey = 'all') { return items.reduce(function(prev, item, i, arr) { @@ -19,6 +19,7 @@ export default function(name, items, blankKey = 'all') { [`${key}IsSelected`]: is('.selected', `[data-test-tab="${name}_${item}"]`), [`${key}Url`]: attribute('href', `[data-test-tab="${name}_${item}"] a`), [key]: clickable(`[data-test-tab="${name}_${item}"] a`), + [`${key}IsVisible`]: isVisible(`[data-test-tab="${name}_${item}"] a`), }, }; }, {}); diff --git a/ui/packages/consul-ui/app/routes/application.js b/ui/packages/consul-ui/app/routes/application.js index 89eb67c53..53c2312c6 100644 --- a/ui/packages/consul-ui/app/routes/application.js +++ b/ui/packages/consul-ui/app/routes/application.js @@ -1,14 +1,44 @@ import Route from 'consul-ui/routing/route'; import { action } from '@ember/object'; import { inject as service } from '@ember/service'; +import { runInDebug } from '@ember/debug'; import WithBlockingActions from 'consul-ui/mixins/with-blocking-actions'; export default class ApplicationRoute extends Route.extend(WithBlockingActions) { @service('client/http') client; + @service('env') env; + @service('repository/token') tokenRepo; + @service('settings') settings; data; + async model() { + if(this.env.var('CONSUL_ACLS_ENABLED')) { + const secret = this.env.var('CONSUL_HTTP_TOKEN'); + const existing = await this.settings.findBySlug('token'); + if(!existing.AccessorID && secret) { + try { + const token = await this.tokenRepo.self({ + secret: secret, + dc: this.env.var('CONSUL_DATACENTER_LOCAL') + }); + await this.settings.persist({ + token: { + AccessorID: token.AccessorID, + SecretID: token.SecretID, + Namespace: token.Namespace, + Partition: token.Partition, + } + }); + } catch(e) { + runInDebug(_ => console.error(e)); + } + } + } + return {}; + } + @action onClientChanged(e) { let data = e.data; diff --git a/ui/packages/consul-ui/app/utils/get-environment.js b/ui/packages/consul-ui/app/utils/get-environment.js index 9fabe8b7a..f7ec678f0 100644 --- a/ui/packages/consul-ui/app/utils/get-environment.js +++ b/ui/packages/consul-ui/app/utils/get-environment.js @@ -132,12 +132,16 @@ export default function(config = {}, win = window, doc = document) { return operatorConfig.LocalDatacenter; case 'CONSUL_DATACENTER_PRIMARY': return operatorConfig.PrimaryDatacenter; + case 'CONSUL_HCP_MANAGED_RUNTIME': + return operatorConfig.HCPManagedRuntime; case 'CONSUL_API_PREFIX': // we want API prefix to look like an env var for if we ever change // operator config to be an API request, we need this variable before we // make and API request so this specific variable should never be be // retrived via an API request return operatorConfig.APIPrefix; + case 'CONSUL_HCP_URL': + return operatorConfig.HCPURL; case 'CONSUL_UI_CONFIG': dashboards = { service: undefined, @@ -190,7 +194,7 @@ export default function(config = {}, win = window, doc = document) { } }; const ui = function(key) { - let $; + let $ = {}; switch (config.environment) { case 'development': case 'staging': @@ -225,15 +229,28 @@ export default function(config = {}, win = window, doc = document) { case 'CONSUL_UI_CONFIG': prev['CONSUL_UI_CONFIG'] = JSON.parse(value); break; + case 'TokenSecretID': + prev['CONSUL_HTTP_TOKEN'] = value; + break; default: prev[key] = value; } return prev; }, {}); - if (typeof $[key] !== 'undefined') { - return $[key]; - } break; + case 'production': + $ = dev().reduce(function(prev, [key, value]) { + switch (key) { + case 'TokenSecretID': + prev['CONSUL_HTTP_TOKEN'] = value; + break; + } + return prev; + }, {}); + break; + } + if (typeof $[key] !== 'undefined') { + return $[key]; } return config[key]; }; @@ -252,7 +269,9 @@ export default function(config = {}, win = window, doc = document) { case 'CONSUL_UI_CONFIG': case 'CONSUL_DATACENTER_LOCAL': case 'CONSUL_DATACENTER_PRIMARY': + case 'CONSUL_HCP_MANAGED_RUNTIME': case 'CONSUL_API_PREFIX': + case 'CONSUL_HCP_URL': case 'CONSUL_ACLS_ENABLED': case 'CONSUL_NSPACES_ENABLED': case 'CONSUL_PEERINGS_ENABLED': diff --git a/ui/packages/consul-ui/package.json b/ui/packages/consul-ui/package.json index 64e731b32..f7fdbab0b 100644 --- a/ui/packages/consul-ui/package.json +++ b/ui/packages/consul-ui/package.json @@ -108,7 +108,7 @@ "ember-cli-flash": "^2.1.1", "ember-cli-htmlbars": "^5.2.0", "ember-cli-inject-live-reload": "^2.0.2", - "ember-cli-page-object": "^1.16.2", + "ember-cli-page-object": "^1.17.10", "ember-cli-sass": "^10.0.1", "ember-cli-sri": "^2.1.1", "ember-cli-string-helpers": "^5.0.0", diff --git a/ui/packages/consul-ui/tests/acceptance/dc/nodes/show.feature b/ui/packages/consul-ui/tests/acceptance/dc/nodes/show.feature index 4ccc4abe9..9c041cd2a 100644 --- a/ui/packages/consul-ui/tests/acceptance/dc/nodes/show.feature +++ b/ui/packages/consul-ui/tests/acceptance/dc/nodes/show.feature @@ -53,11 +53,11 @@ Feature: dc / nodes / show: Show node dc: dc1 node: node-0 --- - And I see healthChecks on the tabs - And I see serviceInstances on the tabs - And I don't see roundTripTime on the tabs - And I see lockSessions on the tabs And I see serviceInstancesIsSelected on the tabs + And I see healthChecksIsVisible on the tabs + And I see serviceInstancesIsVisible on the tabs + And I don't see roundTripTime on the tabs + And I see lockSessionsIsVisible on the tabs Scenario: A node warns when deregistered whilst blocking Given 1 node model from yaml --- diff --git a/ui/packages/consul-ui/tests/acceptance/dc/services/instances/exposed-paths.feature b/ui/packages/consul-ui/tests/acceptance/dc/services/instances/exposed-paths.feature index e688deae0..1635d6114 100644 --- a/ui/packages/consul-ui/tests/acceptance/dc/services/instances/exposed-paths.feature +++ b/ui/packages/consul-ui/tests/acceptance/dc/services/instances/exposed-paths.feature @@ -33,7 +33,7 @@ Feature: dc / services / instances / Exposed Paths id: service-0-with-id --- Then the url should be /dc1/services/service-0/instances/node-0/service-0-with-id/health-checks - And I see exposedPaths on the tabs + And I see exposedPathsIsVisible on the tabs When I click exposedPaths on the tabs diff --git a/ui/packages/consul-ui/tests/acceptance/dc/services/instances/upstreams.feature b/ui/packages/consul-ui/tests/acceptance/dc/services/instances/upstreams.feature index ba86f6992..c80c754f4 100644 --- a/ui/packages/consul-ui/tests/acceptance/dc/services/instances/upstreams.feature +++ b/ui/packages/consul-ui/tests/acceptance/dc/services/instances/upstreams.feature @@ -33,7 +33,7 @@ Feature: dc / services / instances / Upstreams id: service-0-with-id --- Then the url should be /dc1/services/service-0/instances/node-0/service-0-with-id/health-checks - And I see upstreams on the tabs + And I see upstreamsIsVisible on the tabs When I click upstreams on the tabs diff --git a/ui/packages/consul-ui/tests/acceptance/dc/services/show-routing.feature b/ui/packages/consul-ui/tests/acceptance/dc/services/show-routing.feature index 3cbf392f7..f997f131d 100644 --- a/ui/packages/consul-ui/tests/acceptance/dc/services/show-routing.feature +++ b/ui/packages/consul-ui/tests/acceptance/dc/services/show-routing.feature @@ -15,7 +15,7 @@ Feature: dc / services / show-routing: Show Routing for Service service: service-0 --- And the title should be "service-0 - Consul" - And I see routing on the tabs + And I see routingIsVisible on the tabs Scenario: Given connect is disabled, the Routing tab should not display or error Given 2 datacenter models from yaml --- @@ -51,14 +51,7 @@ Feature: dc / services / show-routing: Show Routing for Service dc: dc2 service: service-1 --- - And I see routing on the tabs - # something weird is going on with this test - # without waiting we issue a url reload that - # will make the test timeout. - # waiting will "fix" this - we should look into - # the underlying reason for this soon. This is - # only a quick-fix to land ember-qunit v5. - And pause for 1000 + And I see routingIsVisible on the tabs And I visit the service page for yaml --- dc: dc1 diff --git a/ui/packages/consul-ui/tests/acceptance/dc/services/show-topology.feature b/ui/packages/consul-ui/tests/acceptance/dc/services/show-topology.feature index 54c7bd2ed..ea78a2971 100644 --- a/ui/packages/consul-ui/tests/acceptance/dc/services/show-topology.feature +++ b/ui/packages/consul-ui/tests/acceptance/dc/services/show-topology.feature @@ -14,7 +14,7 @@ Feature: dc / services / show-topology: Show Topology tab for Service dc: dc1 service: service-0 --- - And I see topology on the tabs + And I see topologyIsVisible on the tabs Then the url should be /dc1/services/service-0/topology Scenario: Given connect is disabled, the Topology tab should not display or error Given 1 datacenter model with the value "dc1" diff --git a/ui/packages/consul-ui/tests/acceptance/dc/services/show/intentions/index.feature b/ui/packages/consul-ui/tests/acceptance/dc/services/show/intentions/index.feature index 16e0b2bc6..0148fa6f9 100644 --- a/ui/packages/consul-ui/tests/acceptance/dc/services/show/intentions/index.feature +++ b/ui/packages/consul-ui/tests/acceptance/dc/services/show/intentions/index.feature @@ -38,7 +38,7 @@ Feature: dc / services / show / intentions / index: Intentions per service service: service-0 --- And the title should be "service-0 - Consul" - And I see intentions on the tabs + And I see intentionsIsVisible on the tabs When I click intentions on the tabs And I see intentionsIsSelected on the tabs Scenario: I can see intentions diff --git a/ui/packages/consul-ui/tests/acceptance/dc/services/show/services.feature b/ui/packages/consul-ui/tests/acceptance/dc/services/show/services.feature index 6871bae39..3b19a6995 100644 --- a/ui/packages/consul-ui/tests/acceptance/dc/services/show/services.feature +++ b/ui/packages/consul-ui/tests/acceptance/dc/services/show/services.feature @@ -16,7 +16,7 @@ Feature: dc / services / show / services service: terminating-gateway-1 --- And the title should be "terminating-gateway-1 - Consul" - And I see linkedServices on the tabs + And I see linkedServicesIsVisible on the tabs When I click linkedServices on the tabs And I see linkedServicesIsSelected on the tabs Scenario: Seeing the list of Linked Services diff --git a/ui/packages/consul-ui/tests/acceptance/dc/services/show/tags.feature b/ui/packages/consul-ui/tests/acceptance/dc/services/show/tags.feature index 1632ba180..667e109e5 100644 --- a/ui/packages/consul-ui/tests/acceptance/dc/services/show/tags.feature +++ b/ui/packages/consul-ui/tests/acceptance/dc/services/show/tags.feature @@ -19,7 +19,7 @@ Feature: dc / services / show / tags dc: dc1 service: service --- - And I see tags on the tabs + And I see tagsIsVisible on the tabs When I click tags on the tabs And I see tagsIsSelected on the tabs And I see 3 tag models on the tabs.tagsTab component @@ -42,7 +42,7 @@ Feature: dc / services / show / tags dc: dc1 service: service --- - And I see tags on the tabs + And I see tagsIsVisible on the tabs When I click tags on the tabs And I see tagsIsSelected on the tabs And I see 3 tag models on the tabs.tagsTab component diff --git a/ui/packages/consul-ui/tests/acceptance/dc/services/show/topology/routing-config.feature b/ui/packages/consul-ui/tests/acceptance/dc/services/show/topology/routing-config.feature index 43ff33555..af66842d1 100644 --- a/ui/packages/consul-ui/tests/acceptance/dc/services/show/topology/routing-config.feature +++ b/ui/packages/consul-ui/tests/acceptance/dc/services/show/topology/routing-config.feature @@ -24,7 +24,7 @@ Feature: dc / services / show / topology / routing-config dc: dc1 service: service-0 --- - And I see topology on the tabs + And I see topologyIsVisible on the tabs Scenario: Given the Source is routing config, show Source Type Then I see the text "Routing configuration" in "[data-test-topology-metrics-source-type]" Scenario: Given the Source is routing config, redirect to Routing Config page diff --git a/ui/packages/consul-ui/tests/acceptance/dc/services/show/topology/stats.feature b/ui/packages/consul-ui/tests/acceptance/dc/services/show/topology/stats.feature index 7ac41444f..393df558a 100644 --- a/ui/packages/consul-ui/tests/acceptance/dc/services/show/topology/stats.feature +++ b/ui/packages/consul-ui/tests/acceptance/dc/services/show/topology/stats.feature @@ -14,7 +14,7 @@ Feature: dc / services / show / topology / stats dc: dc1 service: service-0 --- - And I see topology on the tabs + And I see topologyIsVisible on the tabs And I don't see the "[data-test-topology-metrics-stats]" element Scenario: Given metrics is enabled, the Topology tab should display metrics Given 1 datacenter model with the value "dc1" @@ -31,7 +31,7 @@ Feature: dc / services / show / topology / stats dc: dc1 service: service-0 --- - And I see topology on the tabs + And I see topologyIsVisible on the tabs And I see the "[data-test-topology-metrics-stats]" element Scenario: Given metrics is enabled, metrics stats are disabled for an ingress gateway Topology Given 1 datacenter model with the value "dc1" @@ -49,7 +49,7 @@ Feature: dc / services / show / topology / stats dc: dc1 service: ingress-gateway --- - And I see topology on the tabs + And I see topologyIsVisible on the tabs And I don't see the "[data-test-topology-metrics-stats]" element And I see the "[data-test-topology-metrics-status]" element Scenario: Given metrics is enabled, metric stats are disabled for ingress gateway as downstream services @@ -77,7 +77,7 @@ Feature: dc / services / show / topology / stats dc: dc1 service: service-0 --- - And I see topology on the tabs + And I see topologyIsVisible on the tabs And I see the "[data-test-sparkline]" element And I don't see the "[data-test-topology-metrics-downstream-stats]" element diff --git a/ui/packages/consul-ui/tests/acceptance/dc/services/show/upstreams.feature b/ui/packages/consul-ui/tests/acceptance/dc/services/show/upstreams.feature index 00dc65fc0..403535e39 100644 --- a/ui/packages/consul-ui/tests/acceptance/dc/services/show/upstreams.feature +++ b/ui/packages/consul-ui/tests/acceptance/dc/services/show/upstreams.feature @@ -16,7 +16,7 @@ Feature: dc / services / show / upstreams service: ingress-gateway-1 --- And the title should be "ingress-gateway-1 - Consul" - And I see upstreams on the tabs + And I see upstreamsIsVisible on the tabs When I click upstreams on the tabs And I see upstreamsIsSelected on the tabs Scenario: Seeing the list of Upstreams diff --git a/ui/packages/consul-ui/tests/integration/components/consul/hcp/home-test.js b/ui/packages/consul-ui/tests/integration/components/consul/hcp/home-test.js new file mode 100644 index 000000000..a43496860 --- /dev/null +++ b/ui/packages/consul-ui/tests/integration/components/consul/hcp/home-test.js @@ -0,0 +1,2 @@ +// temporary import until we are running as separate applications +import 'consul-ui/components/consul/hcp/home/index.test'; diff --git a/ui/packages/consul-ui/tests/steps/assertions/page.js b/ui/packages/consul-ui/tests/steps/assertions/page.js index ddb680bf2..cc2c8f4b7 100644 --- a/ui/packages/consul-ui/tests/steps/assertions/page.js +++ b/ui/packages/consul-ui/tests/steps/assertions/page.js @@ -184,7 +184,9 @@ export default function(scenario, assert, find, currentPage, $) { } } assert[isNegative ? 'notOk' : 'ok'](target, message); - return Promise.resolve(); + + // always return promise and handle the fact that `target` could be async + return Promise.resolve().then(() => target); }) .then( [ diff --git a/ui/yarn.lock b/ui/yarn.lock index 76bbc42af..cc2fa84a7 100644 --- a/ui/yarn.lock +++ b/ui/yarn.lock @@ -6530,7 +6530,7 @@ ember-cli-babel@^6.0.0, ember-cli-babel@^6.0.0-beta.4, ember-cli-babel@^6.11.0, ember-cli-version-checker "^2.1.2" semver "^5.5.0" -ember-cli-babel@^7.13.2, ember-cli-babel@^7.26.3, ember-cli-babel@^7.26.5: +ember-cli-babel@^7.13.2, ember-cli-babel@^7.26.1, ember-cli-babel@^7.26.3, ember-cli-babel@^7.26.5: version "7.26.11" resolved "https://registry.yarnpkg.com/ember-cli-babel/-/ember-cli-babel-7.26.11.tgz#50da0fe4dcd99aada499843940fec75076249a9f" integrity sha512-JJYeYjiz/JTn34q7F5DSOjkkZqy8qwFOOxXfE6pe9yEJqWGu4qErKxlz8I22JoVEQ/aBUO+OcKTpmctvykM9YA== @@ -6781,15 +6781,15 @@ ember-cli-normalize-entity-name@^1.0.0: dependencies: silent-error "^1.0.0" -ember-cli-page-object@^1.16.2: - version "1.17.7" - resolved "https://registry.yarnpkg.com/ember-cli-page-object/-/ember-cli-page-object-1.17.7.tgz#a35c4cc1ece147e9752604cbc2266038660a84f6" - integrity sha512-sp7lunZa9p57cNm6og86F12SBx5Tt/7dWndfIKGE9Kol3QP2/72qiUVauhbfoUDSjfYLG2xEAFWDbiLHPMPYsg== +ember-cli-page-object@^1.17.10: + version "1.17.10" + resolved "https://registry.yarnpkg.com/ember-cli-page-object/-/ember-cli-page-object-1.17.10.tgz#a3145c7b0341e6180dab28e10c858f8b6535e66a" + integrity sha512-J7OQZ4IWftHLunsCicFbaVb/GrI+/DMWMPO5EAca9+0x9K+rxml351Tl1Z/Fpr8UmHg1q/KGwqGx9xGodrRxbg== dependencies: broccoli-file-creator "^2.1.1" broccoli-merge-trees "^2.0.0" ceibo "~2.0.0" - ember-cli-babel "^6.16.0" + ember-cli-babel "^7.26.1" ember-cli-node-assets "^0.2.2" ember-native-dom-helpers "^0.7.0" jquery "^3.4.1" diff --git a/website/content/api-docs/txn.mdx b/website/content/api-docs/txn.mdx index 97eefeece..a42176cbb 100644 --- a/website/content/api-docs/txn.mdx +++ b/website/content/api-docs/txn.mdx @@ -266,20 +266,21 @@ look like this: The following tables summarize the available verbs and the fields that apply to those operations ("X" means a field is required and "O" means it is optional): -| Verb | Operation | Key | Value | Flags | Index | Session | -| ------------------ | --------------------------------------- | :-: | :---: | :---: | :---: | :-----: | -| `set` | Sets the `Key` to the given `Value` | `x` | `x` | `o` | | | -| `cas` | Sets, but with CAS semantics | `x` | `x` | `o` | `x` | | -| `lock` | Lock with the given `Session` | `x` | `x` | `o` | | `x` | -| `unlock` | Unlock with the given `Session` | `x` | `x` | `o` | | `x` | -| `get` | Get the key, fails if it does not exist | `x` | | | | | -| `get-tree` | Gets all keys with the prefix | `x` | | | | | -| `check-index` | Fail if modify index != index | `x` | | | `x` | | -| `check-session` | Fail if not locked by session | `x` | | | | `x` | -| `check-not-exists` | Fail if key exists | `x` | | | | | -| `delete` | Delete the key | `x` | | | | | -| `delete-tree` | Delete all keys with a prefix | `x` | | | | | -| `delete-cas` | Delete, but with CAS semantics | `x` | | | `x` | | +| Verb | Operation | Key | Value | Flags | Index | Session | +| ------------------ | ----------------------------------------- | :-: | :---: | :---: | :---: | :-----: | +| `set` | Sets the `Key` to the given `Value` | `x` | `x` | `o` | | | +| `cas` | Sets, but with CAS semantics | `x` | `x` | `o` | `x` | | +| `lock` | Lock with the given `Session` | `x` | `x` | `o` | | `x` | +| `unlock` | Unlock with the given `Session` | `x` | `x` | `o` | | `x` | +| `get` | Get the key, fails if it does not exist | `x` | | | | | +| `get-or-empty` | Get the key, or null if it does not exist | `x` | | | | | +| `get-tree` | Gets all keys with the prefix | `x` | | | | | +| `check-index` | Fail if modify index != index | `x` | | | `x` | | +| `check-session` | Fail if not locked by session | `x` | | | | `x` | +| `check-not-exists` | Fail if key exists | `x` | | | | | +| `delete` | Delete the key | `x` | | | | | +| `delete-tree` | Delete all keys with a prefix | `x` | | | | | +| `delete-cas` | Delete, but with CAS semantics | `x` | | | `x` | | #### Node Operations diff --git a/website/content/commands/license.mdx b/website/content/commands/license.mdx index 08ee230b0..6c230a470 100644 --- a/website/content/commands/license.mdx +++ b/website/content/commands/license.mdx @@ -12,8 +12,9 @@ Command: `consul license` -The `license` command provides a datacenter-level view of the Consul Enterprise license. This was added -in Consul 1.1.0 but Consul 1.10.0 removed the ability to set and reset the license using the CLI. +The `license` command provides a list of all datacenters that use the Consul Enterprise license applied to the current datacenter. + +~> **Warning**: Consul 1.10.0 removed the ability to set and reset the license using the CLI. See the [licensing documentation](/docs/enterprise/license/overview) for more information about Consul Enterprise license management. diff --git a/website/content/docs/agent/config/index.mdx b/website/content/docs/agent/config/index.mdx index 11953b506..db77f5dcf 100644 --- a/website/content/docs/agent/config/index.mdx +++ b/website/content/docs/agent/config/index.mdx @@ -90,3 +90,4 @@ items which are reloaded include: Consul will issue the following warning, `Static Runtime config has changed and need a manual config reload to be applied`. You must manually issue the `consul reload` command or send a `SIGHUP` to the Consul process to reload the new values. - Watches +- [License](/docs/enterprise/license/overview) diff --git a/website/content/docs/enterprise/license/overview.mdx b/website/content/docs/enterprise/license/overview.mdx index 8652f357f..8066240d9 100644 --- a/website/content/docs/enterprise/license/overview.mdx +++ b/website/content/docs/enterprise/license/overview.mdx @@ -40,6 +40,13 @@ may also be licensed in the very same manner. However, to avoid the need to configure the license on many client agents and snapshot agents, those agents have the capability to retrieve the license automatically under the conditions described below. +Updating the license for an agent depends on the method you used to apply the license. +- **If you used the `CONSUL_LICENSE` +environment variable**: After updating the environment variable, restart the affected agents. +- **If you used the +`CONSUL_LICENSE_PATH` environment variable**: Update the license file first. Then, restart the affected agents. +- **If you used the `license_path` configuration item**: Update the license file first. Then, run [`consul reload`](/commands/reload) for the affected agents. + #### Client Agent License Retrieval When a client agent starts without a license in its configuration or environment, it will try to retrieve the diff --git a/website/content/docs/k8s/deployment-configurations/single-dc-multi-k8s.mdx b/website/content/docs/k8s/deployment-configurations/single-dc-multi-k8s.mdx index b854ebc3e..7387867ea 100644 --- a/website/content/docs/k8s/deployment-configurations/single-dc-multi-k8s.mdx +++ b/website/content/docs/k8s/deployment-configurations/single-dc-multi-k8s.mdx @@ -6,7 +6,7 @@ description: Single Consul Datacenter deployed in multiple Kubernetes clusters # Single Consul Datacenter in Multiple Kubernetes Clusters -~> **Note:** For running Consul across multiple Kubernetes, it is generally recommended to utilize [Admin Partitions](/docs/enterprise/admin-partitions) for production environments. This Consul Enterprise feature allows for the ability to accommodate for multiple tenants without concerns of resource collisions when administering a cluster at scale, and for the ability to run Consul on Kubernetes clusters across a non-flat network. +~> **Note:** When running Consul across multiple Kubernetes clusters, we recommend using [admin partitions](/docs/enterprise/admin-partitions) for production environments. This Consul Enterprise feature allows you to accommodate multiple tenants without resource collisions when administering a cluster at scale. Admin partitions also enable you to run Consul on Kubernetes clusters across a non-flat network. This page describes deploying a single Consul datacenter in multiple Kubernetes clusters, with servers and clients running in one cluster and only clients in the rest of the clusters. @@ -76,7 +76,7 @@ which are likely going to change. To deploy, first generate the Gossip encryption key and save it as a Kubernetes secret. -```shell +```shell-session $ kubectl create secret generic consul-gossip-encryption-key --from-literal=key=$(consul keygen) ``` @@ -163,7 +163,7 @@ which can be seen by running `kubectl get nodes --output wide`. Set `externalServers.httpsPort` to the `nodePort` of the `cluster1-consul-ui` service. In our example, the port is `31557`. -```shell +```shell-session $ kubectl get service cluster1-consul-ui --context cluster1 NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE cluster1-consul-ui NodePort 10.0.240.80 443:31557/TCP 40h diff --git a/website/content/docs/k8s/installation/install.mdx b/website/content/docs/k8s/installation/install.mdx index 7247013d6..58986117e 100644 --- a/website/content/docs/k8s/installation/install.mdx +++ b/website/content/docs/k8s/installation/install.mdx @@ -109,7 +109,7 @@ NAME: consul ### Enable the Consul CNI plugin -By default, Consul generates a `connect-inject init` container as part of the Kubernetes pod startup process when Consul is in [transparent proxy mode](/docs/connect/transparent-proxy). The container configures traffic redirection in the service mesh through the sidecar proxy. To configure redirection, the container requires elevated CAP_NET_ADMIN privileges, which may not be compatible with security policies in your organization. +By default, Consul generates a `connect-inject init` container as part of the Kubernetes pod startup process when Consul is in [transparent proxy mode](/docs/connect/transparent-proxy). The container configures traffic redirection in the service mesh through the sidecar proxy. To configure redirection, the container requires elevated `CAP_NET_ADMIN` privileges, which may not be compatible with security policies in your organization. Instead, you can enable the Consul container network interface (CNI) plugin to perform traffic redirection. Because the plugin is executed by the Kubernetes kubelet, the plugin already has the elevated privileges necessary to configure the network. @@ -125,10 +125,10 @@ global: connectInject: enabled: true cni: - enabled: true - logLevel: info - cniBinDir: "/opt/cni/bin" - cniNetDir: "/etc/cni/net.d" + enabled: true + logLevel: info + cniBinDir: "/opt/cni/bin" + cniNetDir: "/etc/cni/net.d" ``` @@ -140,10 +140,10 @@ global: connectInject: enabled: true cni: - enabled: true - logLevel: info - cniBinDir: "/home/kubernetes/bin" - cniNetDir: "/etc/cni/net.d" + enabled: true + logLevel: info + cniBinDir: "/home/kubernetes/bin" + cniNetDir: "/etc/cni/net.d" ```