mirror of https://github.com/hashicorp/consul
Merge remote-tracking branch 'hashicorp/main' into feature/health-checks_windows_service
commit
60c7c831c6
|
@ -0,0 +1,3 @@
|
||||||
|
```release-note:bugfix
|
||||||
|
peering: Fix issue preventing deletion and recreation of peerings in TERMINATED state.
|
||||||
|
```
|
|
@ -0,0 +1,3 @@
|
||||||
|
```release-note:improvement
|
||||||
|
xds: Set `max_ejection_percent` on Envoy's outlier detection to 100% for peered services.
|
||||||
|
```
|
|
@ -0,0 +1,5 @@
|
||||||
|
```release-note:bug
|
||||||
|
api: Fix a breaking change caused by renaming `QueryDatacenterOptions` to
|
||||||
|
`QueryFailoverOptions`. This adds `QueryDatacenterOptions` back as an alias to
|
||||||
|
`QueryFailoverOptions` and marks it as deprecated.
|
||||||
|
```
|
|
@ -0,0 +1,3 @@
|
||||||
|
```release-note:feature
|
||||||
|
peering: Add support to failover to services running on cluster peers.
|
||||||
|
```
|
|
@ -0,0 +1,3 @@
|
||||||
|
```release-note:improvement
|
||||||
|
snapshot agent: **(Enterprise only)** Add support for path-based addressing when using s3 backend.
|
||||||
|
```
|
|
@ -816,7 +816,7 @@ jobs:
|
||||||
# Get go binary from workspace
|
# Get go binary from workspace
|
||||||
- attach_workspace:
|
- attach_workspace:
|
||||||
at: .
|
at: .
|
||||||
# Build the consul-dev image from the already built binary
|
# Build the consul:local image from the already built binary
|
||||||
- run:
|
- run:
|
||||||
command: |
|
command: |
|
||||||
sudo rm -rf /usr/local/go
|
sudo rm -rf /usr/local/go
|
||||||
|
@ -887,8 +887,8 @@ jobs:
|
||||||
- attach_workspace:
|
- attach_workspace:
|
||||||
at: .
|
at: .
|
||||||
- run: *install-gotestsum
|
- run: *install-gotestsum
|
||||||
# Build the consul-dev image from the already built binary
|
# Build the consul:local image from the already built binary
|
||||||
- run: docker build -t consul-dev -f ./build-support/docker/Consul-Dev.dockerfile .
|
- run: docker build -t consul:local -f ./build-support/docker/Consul-Dev.dockerfile .
|
||||||
- run:
|
- run:
|
||||||
name: Envoy Integration Tests
|
name: Envoy Integration Tests
|
||||||
command: |
|
command: |
|
||||||
|
@ -902,6 +902,7 @@ jobs:
|
||||||
GOTESTSUM_JUNITFILE: /tmp/test-results/results.xml
|
GOTESTSUM_JUNITFILE: /tmp/test-results/results.xml
|
||||||
GOTESTSUM_FORMAT: standard-verbose
|
GOTESTSUM_FORMAT: standard-verbose
|
||||||
COMPOSE_INTERACTIVE_NO_CLI: 1
|
COMPOSE_INTERACTIVE_NO_CLI: 1
|
||||||
|
LAMBDA_TESTS_ENABLED: "true"
|
||||||
# tput complains if this isn't set to something.
|
# tput complains if this isn't set to something.
|
||||||
TERM: ansi
|
TERM: ansi
|
||||||
- store_artifacts:
|
- store_artifacts:
|
||||||
|
|
48
GNUmakefile
48
GNUmakefile
|
@ -16,6 +16,7 @@ PROTOC_GO_INJECT_TAG_VERSION='v1.3.0'
|
||||||
|
|
||||||
GOTAGS ?=
|
GOTAGS ?=
|
||||||
GOPATH=$(shell go env GOPATH)
|
GOPATH=$(shell go env GOPATH)
|
||||||
|
GOARCH?=$(shell go env GOARCH)
|
||||||
MAIN_GOPATH=$(shell go env GOPATH | cut -d: -f1)
|
MAIN_GOPATH=$(shell go env GOPATH | cut -d: -f1)
|
||||||
|
|
||||||
export PATH := $(PWD)/bin:$(GOPATH)/bin:$(PATH)
|
export PATH := $(PWD)/bin:$(GOPATH)/bin:$(PATH)
|
||||||
|
@ -129,7 +130,7 @@ export GOLDFLAGS
|
||||||
|
|
||||||
# Allow skipping docker build during integration tests in CI since we already
|
# Allow skipping docker build during integration tests in CI since we already
|
||||||
# have a built binary
|
# have a built binary
|
||||||
ENVOY_INTEG_DEPS?=dev-docker
|
ENVOY_INTEG_DEPS?=docker-envoy-integ
|
||||||
ifdef SKIP_DOCKER_BUILD
|
ifdef SKIP_DOCKER_BUILD
|
||||||
ENVOY_INTEG_DEPS=noop
|
ENVOY_INTEG_DEPS=noop
|
||||||
endif
|
endif
|
||||||
|
@ -152,7 +153,28 @@ dev-docker: linux
|
||||||
@docker pull consul:$(CONSUL_IMAGE_VERSION) >/dev/null
|
@docker pull consul:$(CONSUL_IMAGE_VERSION) >/dev/null
|
||||||
@echo "Building Consul Development container - $(CONSUL_DEV_IMAGE)"
|
@echo "Building Consul Development container - $(CONSUL_DEV_IMAGE)"
|
||||||
# 'consul:local' tag is needed to run the integration tests
|
# 'consul:local' tag is needed to run the integration tests
|
||||||
@DOCKER_DEFAULT_PLATFORM=linux/amd64 docker build $(NOCACHE) $(QUIET) -t '$(CONSUL_DEV_IMAGE)' -t 'consul:local' --build-arg CONSUL_IMAGE_VERSION=$(CONSUL_IMAGE_VERSION) $(CURDIR)/pkg/bin/linux_amd64 -f $(CURDIR)/build-support/docker/Consul-Dev.dockerfile
|
@docker buildx use default && docker buildx build -t 'consul:local' \
|
||||||
|
--platform linux/$(GOARCH) \
|
||||||
|
--build-arg CONSUL_IMAGE_VERSION=$(CONSUL_IMAGE_VERSION) \
|
||||||
|
--load \
|
||||||
|
-f $(CURDIR)/build-support/docker/Consul-Dev-Multiarch.dockerfile $(CURDIR)/pkg/bin/
|
||||||
|
|
||||||
|
check-remote-dev-image-env:
|
||||||
|
ifndef REMOTE_DEV_IMAGE
|
||||||
|
$(error REMOTE_DEV_IMAGE is undefined: set this image to <your_docker_repo>/<your_docker_image>:<image_tag>, e.g. hashicorp/consul-k8s-dev:latest)
|
||||||
|
endif
|
||||||
|
|
||||||
|
remote-docker: check-remote-dev-image-env
|
||||||
|
$(MAKE) GOARCH=amd64 linux
|
||||||
|
$(MAKE) GOARCH=arm64 linux
|
||||||
|
@echo "Pulling consul container image - $(CONSUL_IMAGE_VERSION)"
|
||||||
|
@docker pull consul:$(CONSUL_IMAGE_VERSION) >/dev/null
|
||||||
|
@echo "Building and Pushing Consul Development container - $(REMOTE_DEV_IMAGE)"
|
||||||
|
@docker buildx use default && docker buildx build -t '$(REMOTE_DEV_IMAGE)' \
|
||||||
|
--platform linux/amd64,linux/arm64 \
|
||||||
|
--build-arg CONSUL_IMAGE_VERSION=$(CONSUL_IMAGE_VERSION) \
|
||||||
|
--push \
|
||||||
|
-f $(CURDIR)/build-support/docker/Consul-Dev-Multiarch.dockerfile $(CURDIR)/pkg/bin/
|
||||||
|
|
||||||
# In CircleCI, the linux binary will be attached from a previous step at bin/. This make target
|
# In CircleCI, the linux binary will be attached from a previous step at bin/. This make target
|
||||||
# should only run in CI and not locally.
|
# should only run in CI and not locally.
|
||||||
|
@ -174,10 +196,10 @@ ifeq ($(CIRCLE_BRANCH), main)
|
||||||
@docker push $(CI_DEV_DOCKER_NAMESPACE)/$(CI_DEV_DOCKER_IMAGE_NAME):latest
|
@docker push $(CI_DEV_DOCKER_NAMESPACE)/$(CI_DEV_DOCKER_IMAGE_NAME):latest
|
||||||
endif
|
endif
|
||||||
|
|
||||||
# linux builds a linux binary independent of the source platform
|
# linux builds a linux binary compatible with the source platform
|
||||||
linux:
|
linux:
|
||||||
@mkdir -p ./pkg/bin/linux_amd64
|
@mkdir -p ./pkg/bin/linux_$(GOARCH)
|
||||||
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o ./pkg/bin/linux_amd64 -ldflags "$(GOLDFLAGS)" -tags "$(GOTAGS)"
|
CGO_ENABLED=0 GOOS=linux GOARCH=$(GOARCH) go build -o ./pkg/bin/linux_$(GOARCH) -ldflags "$(GOLDFLAGS)" -tags "$(GOTAGS)"
|
||||||
|
|
||||||
# dist builds binaries for all platforms and packages them for distribution
|
# dist builds binaries for all platforms and packages them for distribution
|
||||||
dist:
|
dist:
|
||||||
|
@ -324,8 +346,22 @@ consul-docker: go-build-image
|
||||||
ui-docker: ui-build-image
|
ui-docker: ui-build-image
|
||||||
@$(SHELL) $(CURDIR)/build-support/scripts/build-docker.sh ui
|
@$(SHELL) $(CURDIR)/build-support/scripts/build-docker.sh ui
|
||||||
|
|
||||||
|
# Build image used to run integration tests locally.
|
||||||
|
docker-envoy-integ:
|
||||||
|
$(MAKE) GOARCH=amd64 linux
|
||||||
|
docker build \
|
||||||
|
--platform linux/amd64 $(NOCACHE) $(QUIET) \
|
||||||
|
-t 'consul:local' \
|
||||||
|
--build-arg CONSUL_IMAGE_VERSION=$(CONSUL_IMAGE_VERSION) \
|
||||||
|
$(CURDIR)/pkg/bin/linux_amd64 \
|
||||||
|
-f $(CURDIR)/build-support/docker/Consul-Dev.dockerfile
|
||||||
|
|
||||||
|
# Run integration tests.
|
||||||
|
# Use GO_TEST_FLAGS to run specific tests:
|
||||||
|
# make test-envoy-integ GO_TEST_FLAGS="-run TestEnvoy/case-basic"
|
||||||
|
# NOTE: Always uses amd64 images, even when running on M1 macs, to match CI/CD environment.
|
||||||
test-envoy-integ: $(ENVOY_INTEG_DEPS)
|
test-envoy-integ: $(ENVOY_INTEG_DEPS)
|
||||||
@go test -v -timeout=30m -tags integration ./test/integration/connect/envoy
|
@go test -v -timeout=30m -tags integration $(GO_TEST_FLAGS) ./test/integration/connect/envoy
|
||||||
|
|
||||||
.PHONY: test-compat-integ
|
.PHONY: test-compat-integ
|
||||||
test-compat-integ: dev-docker
|
test-compat-integ: dev-docker
|
||||||
|
|
|
@ -39,6 +39,7 @@ func TestCompile(t *testing.T) {
|
||||||
"service redirect": testcase_ServiceRedirect(),
|
"service redirect": testcase_ServiceRedirect(),
|
||||||
"service and subset redirect": testcase_ServiceAndSubsetRedirect(),
|
"service and subset redirect": testcase_ServiceAndSubsetRedirect(),
|
||||||
"datacenter redirect": testcase_DatacenterRedirect(),
|
"datacenter redirect": testcase_DatacenterRedirect(),
|
||||||
|
"redirect to cluster peer": testcase_PeerRedirect(),
|
||||||
"datacenter redirect with mesh gateways": testcase_DatacenterRedirect_WithMeshGateways(),
|
"datacenter redirect with mesh gateways": testcase_DatacenterRedirect_WithMeshGateways(),
|
||||||
"service failover": testcase_ServiceFailover(),
|
"service failover": testcase_ServiceFailover(),
|
||||||
"service failover through redirect": testcase_ServiceFailoverThroughRedirect(),
|
"service failover through redirect": testcase_ServiceFailoverThroughRedirect(),
|
||||||
|
@ -1084,6 +1085,47 @@ func testcase_DatacenterRedirect() compileTestCase {
|
||||||
return compileTestCase{entries: entries, expect: expect}
|
return compileTestCase{entries: entries, expect: expect}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func testcase_PeerRedirect() compileTestCase {
|
||||||
|
entries := newEntries()
|
||||||
|
entries.AddResolvers(
|
||||||
|
&structs.ServiceResolverConfigEntry{
|
||||||
|
Kind: "service-resolver",
|
||||||
|
Name: "main",
|
||||||
|
Redirect: &structs.ServiceResolverRedirect{
|
||||||
|
Service: "other",
|
||||||
|
Peer: "cluster-01",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
expect := &structs.CompiledDiscoveryChain{
|
||||||
|
Protocol: "tcp",
|
||||||
|
StartNode: "resolver:other.default.default.external.cluster-01",
|
||||||
|
Nodes: map[string]*structs.DiscoveryGraphNode{
|
||||||
|
"resolver:other.default.default.external.cluster-01": {
|
||||||
|
Type: structs.DiscoveryGraphNodeTypeResolver,
|
||||||
|
Name: "other.default.default.external.cluster-01",
|
||||||
|
Resolver: &structs.DiscoveryResolver{
|
||||||
|
Default: true,
|
||||||
|
ConnectTimeout: 5 * time.Second,
|
||||||
|
Target: "other.default.default.external.cluster-01",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Targets: map[string]*structs.DiscoveryTarget{
|
||||||
|
"other.default.default.external.cluster-01": newTarget(structs.DiscoveryTargetOpts{
|
||||||
|
Service: "other",
|
||||||
|
Peer: "cluster-01",
|
||||||
|
}, func(t *structs.DiscoveryTarget) {
|
||||||
|
t.SNI = ""
|
||||||
|
t.Name = ""
|
||||||
|
t.Datacenter = ""
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
return compileTestCase{entries: entries, expect: expect}
|
||||||
|
}
|
||||||
|
|
||||||
func testcase_DatacenterRedirect_WithMeshGateways() compileTestCase {
|
func testcase_DatacenterRedirect_WithMeshGateways() compileTestCase {
|
||||||
entries := newEntries()
|
entries := newEntries()
|
||||||
entries.AddProxyDefaults(&structs.ProxyConfigEntry{
|
entries.AddProxyDefaults(&structs.ProxyConfigEntry{
|
||||||
|
|
|
@ -112,7 +112,7 @@ func (s *Server) emitPeeringMetricsOnce(logger hclog.Logger, metricsImpl *metric
|
||||||
if status.NeverConnected {
|
if status.NeverConnected {
|
||||||
metricsImpl.SetGaugeWithLabels(leaderHealthyPeeringKey, float32(math.NaN()), labels)
|
metricsImpl.SetGaugeWithLabels(leaderHealthyPeeringKey, float32(math.NaN()), labels)
|
||||||
} else {
|
} else {
|
||||||
healthy := status.IsHealthy()
|
healthy := s.peerStreamServer.Tracker.IsHealthy(status)
|
||||||
healthyInt := 0
|
healthyInt := 0
|
||||||
if healthy {
|
if healthy {
|
||||||
healthyInt = 1
|
healthyInt = 1
|
||||||
|
@ -305,7 +305,7 @@ func (s *Server) establishStream(ctx context.Context, logger hclog.Logger, ws me
|
||||||
|
|
||||||
logger.Trace("establishing stream to peer")
|
logger.Trace("establishing stream to peer")
|
||||||
|
|
||||||
streamStatus, err := s.peerStreamTracker.Register(peer.ID)
|
streamStatus, err := s.peerStreamServer.Tracker.Register(peer.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to register stream: %v", err)
|
return fmt.Errorf("failed to register stream: %v", err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,6 +40,7 @@ func TestLeader_PeeringSync_Lifecycle_ClientDeletion(t *testing.T) {
|
||||||
testLeader_PeeringSync_Lifecycle_ClientDeletion(t, true)
|
testLeader_PeeringSync_Lifecycle_ClientDeletion(t, true)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func testLeader_PeeringSync_Lifecycle_ClientDeletion(t *testing.T, enableTLS bool) {
|
func testLeader_PeeringSync_Lifecycle_ClientDeletion(t *testing.T, enableTLS bool) {
|
||||||
if testing.Short() {
|
if testing.Short() {
|
||||||
t.Skip("too slow for testing.Short")
|
t.Skip("too slow for testing.Short")
|
||||||
|
@ -137,9 +138,11 @@ func testLeader_PeeringSync_Lifecycle_ClientDeletion(t *testing.T, enableTLS boo
|
||||||
|
|
||||||
// Delete the peering to trigger the termination sequence.
|
// Delete the peering to trigger the termination sequence.
|
||||||
deleted := &pbpeering.Peering{
|
deleted := &pbpeering.Peering{
|
||||||
ID: p.Peering.ID,
|
ID: p.Peering.ID,
|
||||||
Name: "my-peer-acceptor",
|
Name: "my-peer-acceptor",
|
||||||
DeletedAt: structs.TimeToProto(time.Now()),
|
State: pbpeering.PeeringState_DELETING,
|
||||||
|
PeerServerAddresses: p.Peering.PeerServerAddresses,
|
||||||
|
DeletedAt: structs.TimeToProto(time.Now()),
|
||||||
}
|
}
|
||||||
require.NoError(t, dialer.fsm.State().PeeringWrite(2000, &pbpeering.PeeringWriteRequest{Peering: deleted}))
|
require.NoError(t, dialer.fsm.State().PeeringWrite(2000, &pbpeering.PeeringWriteRequest{Peering: deleted}))
|
||||||
dialer.logger.Trace("deleted peering for my-peer-acceptor")
|
dialer.logger.Trace("deleted peering for my-peer-acceptor")
|
||||||
|
@ -262,6 +265,7 @@ func testLeader_PeeringSync_Lifecycle_AcceptorDeletion(t *testing.T, enableTLS b
|
||||||
deleted := &pbpeering.Peering{
|
deleted := &pbpeering.Peering{
|
||||||
ID: p.Peering.PeerID,
|
ID: p.Peering.PeerID,
|
||||||
Name: "my-peer-dialer",
|
Name: "my-peer-dialer",
|
||||||
|
State: pbpeering.PeeringState_DELETING,
|
||||||
DeletedAt: structs.TimeToProto(time.Now()),
|
DeletedAt: structs.TimeToProto(time.Now()),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -431,6 +435,7 @@ func TestLeader_Peering_DeferredDeletion(t *testing.T) {
|
||||||
Peering: &pbpeering.Peering{
|
Peering: &pbpeering.Peering{
|
||||||
ID: peerID,
|
ID: peerID,
|
||||||
Name: peerName,
|
Name: peerName,
|
||||||
|
State: pbpeering.PeeringState_DELETING,
|
||||||
DeletedAt: structs.TimeToProto(time.Now()),
|
DeletedAt: structs.TimeToProto(time.Now()),
|
||||||
},
|
},
|
||||||
}))
|
}))
|
||||||
|
@ -1165,6 +1170,7 @@ func TestLeader_Peering_NoDeletionWhenPeeringDisabled(t *testing.T) {
|
||||||
Peering: &pbpeering.Peering{
|
Peering: &pbpeering.Peering{
|
||||||
ID: peerID,
|
ID: peerID,
|
||||||
Name: peerName,
|
Name: peerName,
|
||||||
|
State: pbpeering.PeeringState_DELETING,
|
||||||
DeletedAt: structs.TimeToProto(time.Now()),
|
DeletedAt: structs.TimeToProto(time.Now()),
|
||||||
},
|
},
|
||||||
}))
|
}))
|
||||||
|
@ -1216,7 +1222,7 @@ func TestLeader_Peering_NoEstablishmentWhenPeeringDisabled(t *testing.T) {
|
||||||
}))
|
}))
|
||||||
|
|
||||||
require.Never(t, func() bool {
|
require.Never(t, func() bool {
|
||||||
_, found := s1.peerStreamTracker.StreamStatus(peerID)
|
_, found := s1.peerStreamServer.StreamStatus(peerID)
|
||||||
return found
|
return found
|
||||||
}, 7*time.Second, 1*time.Second, "peering should not have been established")
|
}, 7*time.Second, 1*time.Second, "peering should not have been established")
|
||||||
}
|
}
|
||||||
|
|
|
@ -370,9 +370,9 @@ type Server struct {
|
||||||
|
|
||||||
// peerStreamServer is a server used to handle peering streams from external clusters.
|
// peerStreamServer is a server used to handle peering streams from external clusters.
|
||||||
peerStreamServer *peerstream.Server
|
peerStreamServer *peerstream.Server
|
||||||
|
|
||||||
// peeringServer handles peering RPC requests internal to this cluster, like generating peering tokens.
|
// peeringServer handles peering RPC requests internal to this cluster, like generating peering tokens.
|
||||||
peeringServer *peering.Server
|
peeringServer *peering.Server
|
||||||
peerStreamTracker *peerstream.Tracker
|
|
||||||
|
|
||||||
// embedded struct to hold all the enterprise specific data
|
// embedded struct to hold all the enterprise specific data
|
||||||
EnterpriseServer
|
EnterpriseServer
|
||||||
|
@ -724,11 +724,9 @@ func NewServer(config *Config, flat Deps, externalGRPCServer *grpc.Server) (*Ser
|
||||||
Logger: logger.Named("grpc-api.server-discovery"),
|
Logger: logger.Named("grpc-api.server-discovery"),
|
||||||
}).Register(s.externalGRPCServer)
|
}).Register(s.externalGRPCServer)
|
||||||
|
|
||||||
s.peerStreamTracker = peerstream.NewTracker()
|
|
||||||
s.peeringBackend = NewPeeringBackend(s)
|
s.peeringBackend = NewPeeringBackend(s)
|
||||||
s.peerStreamServer = peerstream.NewServer(peerstream.Config{
|
s.peerStreamServer = peerstream.NewServer(peerstream.Config{
|
||||||
Backend: s.peeringBackend,
|
Backend: s.peeringBackend,
|
||||||
Tracker: s.peerStreamTracker,
|
|
||||||
GetStore: func() peerstream.StateStore { return s.FSM().State() },
|
GetStore: func() peerstream.StateStore { return s.FSM().State() },
|
||||||
Logger: logger.Named("grpc-api.peerstream"),
|
Logger: logger.Named("grpc-api.peerstream"),
|
||||||
ACLResolver: s.ACLResolver,
|
ACLResolver: s.ACLResolver,
|
||||||
|
@ -742,7 +740,6 @@ func NewServer(config *Config, flat Deps, externalGRPCServer *grpc.Server) (*Ser
|
||||||
return s.ForwardGRPC(s.grpcConnPool, info, fn)
|
return s.ForwardGRPC(s.grpcConnPool, info, fn)
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
s.peerStreamTracker.SetHeartbeatTimeout(s.peerStreamServer.Config.IncomingHeartbeatTimeout)
|
|
||||||
s.peerStreamServer.Register(s.externalGRPCServer)
|
s.peerStreamServer.Register(s.externalGRPCServer)
|
||||||
|
|
||||||
// Initialize internal gRPC server.
|
// Initialize internal gRPC server.
|
||||||
|
@ -791,7 +788,7 @@ func newGRPCHandlerFromConfig(deps Deps, config *Config, s *Server) connHandler
|
||||||
|
|
||||||
p := peering.NewServer(peering.Config{
|
p := peering.NewServer(peering.Config{
|
||||||
Backend: s.peeringBackend,
|
Backend: s.peeringBackend,
|
||||||
Tracker: s.peerStreamTracker,
|
Tracker: s.peerStreamServer.Tracker,
|
||||||
Logger: deps.Logger.Named("grpc-api.peering"),
|
Logger: deps.Logger.Named("grpc-api.peering"),
|
||||||
ForwardRPC: func(info structs.RPCInfo, fn func(*grpc.ClientConn) error) (bool, error) {
|
ForwardRPC: func(info structs.RPCInfo, fn func(*grpc.ClientConn) error) (bool, error) {
|
||||||
// Only forward the request if the dc in the request matches the server's datacenter.
|
// Only forward the request if the dc in the request matches the server's datacenter.
|
||||||
|
@ -1575,12 +1572,12 @@ func (s *Server) Stats() map[string]map[string]string {
|
||||||
// GetLANCoordinate returns the coordinate of the node in the LAN gossip
|
// GetLANCoordinate returns the coordinate of the node in the LAN gossip
|
||||||
// pool.
|
// pool.
|
||||||
//
|
//
|
||||||
// - Clients return a single coordinate for the single gossip pool they are
|
// - Clients return a single coordinate for the single gossip pool they are
|
||||||
// in (default, segment, or partition).
|
// in (default, segment, or partition).
|
||||||
//
|
//
|
||||||
// - Servers return one coordinate for their canonical gossip pool (i.e.
|
// - Servers return one coordinate for their canonical gossip pool (i.e.
|
||||||
// default partition/segment) and one per segment they are also ancillary
|
// default partition/segment) and one per segment they are also ancillary
|
||||||
// members of.
|
// members of.
|
||||||
//
|
//
|
||||||
// NOTE: servers do not emit coordinates for partitioned gossip pools they
|
// NOTE: servers do not emit coordinates for partitioned gossip pools they
|
||||||
// are ancillary members of.
|
// are ancillary members of.
|
||||||
|
|
|
@ -535,6 +535,12 @@ func (s *Store) PeeringWrite(idx uint64, req *pbpeering.PeeringWriteRequest) err
|
||||||
if req.Peering.Name == "" {
|
if req.Peering.Name == "" {
|
||||||
return errors.New("Missing Peering Name")
|
return errors.New("Missing Peering Name")
|
||||||
}
|
}
|
||||||
|
if req.Peering.State == pbpeering.PeeringState_DELETING && (req.Peering.DeletedAt == nil || structs.IsZeroProtoTime(req.Peering.DeletedAt)) {
|
||||||
|
return errors.New("Missing deletion time for peering in deleting state")
|
||||||
|
}
|
||||||
|
if req.Peering.DeletedAt != nil && !structs.IsZeroProtoTime(req.Peering.DeletedAt) && req.Peering.State != pbpeering.PeeringState_DELETING {
|
||||||
|
return fmt.Errorf("Unexpected state for peering with deletion time: %s", pbpeering.PeeringStateToAPI(req.Peering.State))
|
||||||
|
}
|
||||||
|
|
||||||
// Ensure the name is unique (cannot conflict with another peering with a different ID).
|
// Ensure the name is unique (cannot conflict with another peering with a different ID).
|
||||||
_, existing, err := peeringReadTxn(tx, nil, Query{
|
_, existing, err := peeringReadTxn(tx, nil, Query{
|
||||||
|
@ -546,11 +552,32 @@ func (s *Store) PeeringWrite(idx uint64, req *pbpeering.PeeringWriteRequest) err
|
||||||
}
|
}
|
||||||
|
|
||||||
if existing != nil {
|
if existing != nil {
|
||||||
|
if req.Peering.ShouldDial() != existing.ShouldDial() {
|
||||||
|
return fmt.Errorf("Cannot switch peering dialing mode from %t to %t", existing.ShouldDial(), req.Peering.ShouldDial())
|
||||||
|
}
|
||||||
|
|
||||||
if req.Peering.ID != existing.ID {
|
if req.Peering.ID != existing.ID {
|
||||||
return fmt.Errorf("A peering already exists with the name %q and a different ID %q", req.Peering.Name, existing.ID)
|
return fmt.Errorf("A peering already exists with the name %q and a different ID %q", req.Peering.Name, existing.ID)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Nothing to do if our peer wants to terminate the peering but the peering is already marked for deletion.
|
||||||
|
if existing.State == pbpeering.PeeringState_DELETING && req.Peering.State == pbpeering.PeeringState_TERMINATED {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// No-op deletion
|
||||||
|
if existing.State == pbpeering.PeeringState_DELETING && req.Peering.State == pbpeering.PeeringState_DELETING {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// No-op termination
|
||||||
|
if existing.State == pbpeering.PeeringState_TERMINATED && req.Peering.State == pbpeering.PeeringState_TERMINATED {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// Prevent modifications to Peering marked for deletion.
|
// Prevent modifications to Peering marked for deletion.
|
||||||
if !existing.IsActive() {
|
// This blocks generating new peering tokens or re-establishing the peering until the peering is done deleting.
|
||||||
|
if existing.State == pbpeering.PeeringState_DELETING {
|
||||||
return fmt.Errorf("cannot write to peering that is marked for deletion")
|
return fmt.Errorf("cannot write to peering that is marked for deletion")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -582,8 +609,8 @@ func (s *Store) PeeringWrite(idx uint64, req *pbpeering.PeeringWriteRequest) err
|
||||||
req.Peering.ModifyIndex = idx
|
req.Peering.ModifyIndex = idx
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure associated secrets are cleaned up when a peering is marked for deletion.
|
// Ensure associated secrets are cleaned up when a peering is marked for deletion or terminated.
|
||||||
if req.Peering.State == pbpeering.PeeringState_DELETING {
|
if !req.Peering.IsActive() {
|
||||||
if err := peeringSecretsDeleteTxn(tx, req.Peering.ID, req.Peering.ShouldDial()); err != nil {
|
if err := peeringSecretsDeleteTxn(tx, req.Peering.ID, req.Peering.ShouldDial()); err != nil {
|
||||||
return fmt.Errorf("failed to delete peering secrets: %w", err)
|
return fmt.Errorf("failed to delete peering secrets: %w", err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -950,6 +950,7 @@ func TestStore_Peering_Watch(t *testing.T) {
|
||||||
Peering: &pbpeering.Peering{
|
Peering: &pbpeering.Peering{
|
||||||
ID: testFooPeerID,
|
ID: testFooPeerID,
|
||||||
Name: "foo",
|
Name: "foo",
|
||||||
|
State: pbpeering.PeeringState_DELETING,
|
||||||
DeletedAt: structs.TimeToProto(time.Now()),
|
DeletedAt: structs.TimeToProto(time.Now()),
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
@ -976,6 +977,7 @@ func TestStore_Peering_Watch(t *testing.T) {
|
||||||
err := s.PeeringWrite(lastIdx, &pbpeering.PeeringWriteRequest{Peering: &pbpeering.Peering{
|
err := s.PeeringWrite(lastIdx, &pbpeering.PeeringWriteRequest{Peering: &pbpeering.Peering{
|
||||||
ID: testBarPeerID,
|
ID: testBarPeerID,
|
||||||
Name: "bar",
|
Name: "bar",
|
||||||
|
State: pbpeering.PeeringState_DELETING,
|
||||||
DeletedAt: structs.TimeToProto(time.Now()),
|
DeletedAt: structs.TimeToProto(time.Now()),
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
@ -1077,6 +1079,7 @@ func TestStore_PeeringList_Watch(t *testing.T) {
|
||||||
Peering: &pbpeering.Peering{
|
Peering: &pbpeering.Peering{
|
||||||
ID: testFooPeerID,
|
ID: testFooPeerID,
|
||||||
Name: "foo",
|
Name: "foo",
|
||||||
|
State: pbpeering.PeeringState_DELETING,
|
||||||
DeletedAt: structs.TimeToProto(time.Now()),
|
DeletedAt: structs.TimeToProto(time.Now()),
|
||||||
Partition: structs.NodeEnterpriseMetaInDefaultPartition().PartitionOrEmpty(),
|
Partition: structs.NodeEnterpriseMetaInDefaultPartition().PartitionOrEmpty(),
|
||||||
},
|
},
|
||||||
|
@ -1112,16 +1115,22 @@ func TestStore_PeeringWrite(t *testing.T) {
|
||||||
// Each case depends on the previous.
|
// Each case depends on the previous.
|
||||||
s := NewStateStore(nil)
|
s := NewStateStore(nil)
|
||||||
|
|
||||||
|
testTime := time.Now()
|
||||||
|
|
||||||
|
type expectations struct {
|
||||||
|
peering *pbpeering.Peering
|
||||||
|
secrets *pbpeering.PeeringSecrets
|
||||||
|
err string
|
||||||
|
}
|
||||||
type testcase struct {
|
type testcase struct {
|
||||||
name string
|
name string
|
||||||
input *pbpeering.PeeringWriteRequest
|
input *pbpeering.PeeringWriteRequest
|
||||||
expectSecrets *pbpeering.PeeringSecrets
|
expect expectations
|
||||||
expectErr string
|
|
||||||
}
|
}
|
||||||
run := func(t *testing.T, tc testcase) {
|
run := func(t *testing.T, tc testcase) {
|
||||||
err := s.PeeringWrite(10, tc.input)
|
err := s.PeeringWrite(10, tc.input)
|
||||||
if tc.expectErr != "" {
|
if tc.expect.err != "" {
|
||||||
testutil.RequireErrorContains(t, err, tc.expectErr)
|
testutil.RequireErrorContains(t, err, tc.expect.err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
@ -1133,52 +1142,176 @@ func TestStore_PeeringWrite(t *testing.T) {
|
||||||
_, p, err := s.PeeringRead(nil, q)
|
_, p, err := s.PeeringRead(nil, q)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NotNil(t, p)
|
require.NotNil(t, p)
|
||||||
require.Equal(t, tc.input.Peering.State, p.State)
|
require.Equal(t, tc.expect.peering.State, p.State)
|
||||||
require.Equal(t, tc.input.Peering.Name, p.Name)
|
require.Equal(t, tc.expect.peering.Name, p.Name)
|
||||||
|
require.Equal(t, tc.expect.peering.Meta, p.Meta)
|
||||||
|
if tc.expect.peering.DeletedAt != nil {
|
||||||
|
require.Equal(t, tc.expect.peering.DeletedAt, p.DeletedAt)
|
||||||
|
}
|
||||||
|
|
||||||
secrets, err := s.PeeringSecretsRead(nil, tc.input.Peering.ID)
|
secrets, err := s.PeeringSecretsRead(nil, tc.input.Peering.ID)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
prototest.AssertDeepEqual(t, tc.expectSecrets, secrets)
|
prototest.AssertDeepEqual(t, tc.expect.secrets, secrets)
|
||||||
}
|
}
|
||||||
tcs := []testcase{
|
tcs := []testcase{
|
||||||
{
|
{
|
||||||
name: "create baz",
|
name: "create baz",
|
||||||
input: &pbpeering.PeeringWriteRequest{
|
input: &pbpeering.PeeringWriteRequest{
|
||||||
Peering: &pbpeering.Peering{
|
Peering: &pbpeering.Peering{
|
||||||
ID: testBazPeerID,
|
ID: testBazPeerID,
|
||||||
Name: "baz",
|
Name: "baz",
|
||||||
Partition: structs.NodeEnterpriseMetaInDefaultPartition().PartitionOrEmpty(),
|
State: pbpeering.PeeringState_ESTABLISHING,
|
||||||
|
PeerServerAddresses: []string{"localhost:8502"},
|
||||||
|
Partition: structs.NodeEnterpriseMetaInDefaultPartition().PartitionOrEmpty(),
|
||||||
},
|
},
|
||||||
SecretsRequest: &pbpeering.SecretsWriteRequest{
|
SecretsRequest: &pbpeering.SecretsWriteRequest{
|
||||||
PeerID: testBazPeerID,
|
PeerID: testBazPeerID,
|
||||||
Request: &pbpeering.SecretsWriteRequest_GenerateToken{
|
Request: &pbpeering.SecretsWriteRequest_Establish{
|
||||||
GenerateToken: &pbpeering.SecretsWriteRequest_GenerateTokenRequest{
|
Establish: &pbpeering.SecretsWriteRequest_EstablishRequest{
|
||||||
EstablishmentSecret: testBazSecretID,
|
ActiveStreamSecret: testBazSecretID,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
expectSecrets: &pbpeering.PeeringSecrets{
|
expect: expectations{
|
||||||
PeerID: testBazPeerID,
|
peering: &pbpeering.Peering{
|
||||||
Establishment: &pbpeering.PeeringSecrets_Establishment{
|
ID: testBazPeerID,
|
||||||
SecretID: testBazSecretID,
|
Name: "baz",
|
||||||
|
State: pbpeering.PeeringState_ESTABLISHING,
|
||||||
},
|
},
|
||||||
|
secrets: &pbpeering.PeeringSecrets{
|
||||||
|
PeerID: testBazPeerID,
|
||||||
|
Stream: &pbpeering.PeeringSecrets_Stream{
|
||||||
|
ActiveSecretID: testBazSecretID,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "cannot change ID for baz",
|
||||||
|
input: &pbpeering.PeeringWriteRequest{
|
||||||
|
Peering: &pbpeering.Peering{
|
||||||
|
ID: "123",
|
||||||
|
Name: "baz",
|
||||||
|
State: pbpeering.PeeringState_FAILING,
|
||||||
|
PeerServerAddresses: []string{"localhost:8502"},
|
||||||
|
Partition: structs.NodeEnterpriseMetaInDefaultPartition().PartitionOrEmpty(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expect: expectations{
|
||||||
|
err: `A peering already exists with the name "baz" and a different ID`,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "cannot change dialer status for baz",
|
||||||
|
input: &pbpeering.PeeringWriteRequest{
|
||||||
|
Peering: &pbpeering.Peering{
|
||||||
|
ID: "123",
|
||||||
|
Name: "baz",
|
||||||
|
State: pbpeering.PeeringState_FAILING,
|
||||||
|
// Excluding the peer server addresses leads to baz not being considered a dialer.
|
||||||
|
// PeerServerAddresses: []string{"localhost:8502"},
|
||||||
|
Partition: structs.NodeEnterpriseMetaInDefaultPartition().PartitionOrEmpty(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expect: expectations{
|
||||||
|
err: "Cannot switch peering dialing mode from true to false",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "update baz",
|
name: "update baz",
|
||||||
input: &pbpeering.PeeringWriteRequest{
|
input: &pbpeering.PeeringWriteRequest{
|
||||||
Peering: &pbpeering.Peering{
|
Peering: &pbpeering.Peering{
|
||||||
ID: testBazPeerID,
|
ID: testBazPeerID,
|
||||||
Name: "baz",
|
Name: "baz",
|
||||||
State: pbpeering.PeeringState_FAILING,
|
State: pbpeering.PeeringState_FAILING,
|
||||||
Partition: structs.NodeEnterpriseMetaInDefaultPartition().PartitionOrEmpty(),
|
PeerServerAddresses: []string{"localhost:8502"},
|
||||||
|
Partition: structs.NodeEnterpriseMetaInDefaultPartition().PartitionOrEmpty(),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
expectSecrets: &pbpeering.PeeringSecrets{
|
expect: expectations{
|
||||||
PeerID: testBazPeerID,
|
peering: &pbpeering.Peering{
|
||||||
Establishment: &pbpeering.PeeringSecrets_Establishment{
|
ID: testBazPeerID,
|
||||||
SecretID: testBazSecretID,
|
Name: "baz",
|
||||||
|
State: pbpeering.PeeringState_FAILING,
|
||||||
|
},
|
||||||
|
secrets: &pbpeering.PeeringSecrets{
|
||||||
|
PeerID: testBazPeerID,
|
||||||
|
Stream: &pbpeering.PeeringSecrets_Stream{
|
||||||
|
ActiveSecretID: testBazSecretID,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "if no state was included in request it is inherited from existing",
|
||||||
|
input: &pbpeering.PeeringWriteRequest{
|
||||||
|
Peering: &pbpeering.Peering{
|
||||||
|
ID: testBazPeerID,
|
||||||
|
Name: "baz",
|
||||||
|
// Send undefined state.
|
||||||
|
// State: pbpeering.PeeringState_FAILING,
|
||||||
|
PeerServerAddresses: []string{"localhost:8502"},
|
||||||
|
Partition: structs.NodeEnterpriseMetaInDefaultPartition().PartitionOrEmpty(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expect: expectations{
|
||||||
|
peering: &pbpeering.Peering{
|
||||||
|
ID: testBazPeerID,
|
||||||
|
Name: "baz",
|
||||||
|
// Previous failing state is picked up.
|
||||||
|
State: pbpeering.PeeringState_FAILING,
|
||||||
|
},
|
||||||
|
secrets: &pbpeering.PeeringSecrets{
|
||||||
|
PeerID: testBazPeerID,
|
||||||
|
Stream: &pbpeering.PeeringSecrets_Stream{
|
||||||
|
ActiveSecretID: testBazSecretID,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "mark baz as terminated",
|
||||||
|
input: &pbpeering.PeeringWriteRequest{
|
||||||
|
Peering: &pbpeering.Peering{
|
||||||
|
ID: testBazPeerID,
|
||||||
|
Name: "baz",
|
||||||
|
State: pbpeering.PeeringState_TERMINATED,
|
||||||
|
PeerServerAddresses: []string{"localhost:8502"},
|
||||||
|
Partition: structs.NodeEnterpriseMetaInDefaultPartition().PartitionOrEmpty(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expect: expectations{
|
||||||
|
peering: &pbpeering.Peering{
|
||||||
|
ID: testBazPeerID,
|
||||||
|
Name: "baz",
|
||||||
|
State: pbpeering.PeeringState_TERMINATED,
|
||||||
|
},
|
||||||
|
// Secrets for baz should have been deleted
|
||||||
|
secrets: nil,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "cannot modify peering during no-op termination",
|
||||||
|
input: &pbpeering.PeeringWriteRequest{
|
||||||
|
Peering: &pbpeering.Peering{
|
||||||
|
ID: testBazPeerID,
|
||||||
|
Name: "baz",
|
||||||
|
State: pbpeering.PeeringState_TERMINATED,
|
||||||
|
Partition: structs.NodeEnterpriseMetaInDefaultPartition().PartitionOrEmpty(),
|
||||||
|
PeerServerAddresses: []string{"localhost:8502"},
|
||||||
|
|
||||||
|
// Attempt to add metadata
|
||||||
|
Meta: map[string]string{"foo": "bar"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expect: expectations{
|
||||||
|
peering: &pbpeering.Peering{
|
||||||
|
ID: testBazPeerID,
|
||||||
|
Name: "baz",
|
||||||
|
State: pbpeering.PeeringState_TERMINATED,
|
||||||
|
// Meta should be unchanged.
|
||||||
|
Meta: nil,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -1186,42 +1319,104 @@ func TestStore_PeeringWrite(t *testing.T) {
|
||||||
name: "mark baz for deletion",
|
name: "mark baz for deletion",
|
||||||
input: &pbpeering.PeeringWriteRequest{
|
input: &pbpeering.PeeringWriteRequest{
|
||||||
Peering: &pbpeering.Peering{
|
Peering: &pbpeering.Peering{
|
||||||
|
ID: testBazPeerID,
|
||||||
|
Name: "baz",
|
||||||
|
State: pbpeering.PeeringState_DELETING,
|
||||||
|
PeerServerAddresses: []string{"localhost:8502"},
|
||||||
|
DeletedAt: structs.TimeToProto(testTime),
|
||||||
|
Partition: structs.NodeEnterpriseMetaInDefaultPartition().PartitionOrEmpty(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expect: expectations{
|
||||||
|
peering: &pbpeering.Peering{
|
||||||
ID: testBazPeerID,
|
ID: testBazPeerID,
|
||||||
Name: "baz",
|
Name: "baz",
|
||||||
State: pbpeering.PeeringState_DELETING,
|
State: pbpeering.PeeringState_DELETING,
|
||||||
DeletedAt: structs.TimeToProto(time.Now()),
|
DeletedAt: structs.TimeToProto(testTime),
|
||||||
Partition: structs.NodeEnterpriseMetaInDefaultPartition().PartitionOrEmpty(),
|
},
|
||||||
|
secrets: nil,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "deleting a deleted peering is a no-op",
|
||||||
|
input: &pbpeering.PeeringWriteRequest{
|
||||||
|
Peering: &pbpeering.Peering{
|
||||||
|
ID: testBazPeerID,
|
||||||
|
Name: "baz",
|
||||||
|
State: pbpeering.PeeringState_DELETING,
|
||||||
|
PeerServerAddresses: []string{"localhost:8502"},
|
||||||
|
DeletedAt: structs.TimeToProto(time.Now()),
|
||||||
|
Partition: structs.NodeEnterpriseMetaInDefaultPartition().PartitionOrEmpty(),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
// Secrets for baz should have been deleted
|
expect: expectations{
|
||||||
expectSecrets: nil,
|
peering: &pbpeering.Peering{
|
||||||
|
ID: testBazPeerID,
|
||||||
|
Name: "baz",
|
||||||
|
// Still marked as deleting at the original testTime
|
||||||
|
State: pbpeering.PeeringState_DELETING,
|
||||||
|
DeletedAt: structs.TimeToProto(testTime),
|
||||||
|
},
|
||||||
|
// Secrets for baz should have been deleted
|
||||||
|
secrets: nil,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "terminating a peering marked for deletion is a no-op",
|
||||||
|
input: &pbpeering.PeeringWriteRequest{
|
||||||
|
Peering: &pbpeering.Peering{
|
||||||
|
ID: testBazPeerID,
|
||||||
|
Name: "baz",
|
||||||
|
State: pbpeering.PeeringState_TERMINATED,
|
||||||
|
PeerServerAddresses: []string{"localhost:8502"},
|
||||||
|
Partition: structs.NodeEnterpriseMetaInDefaultPartition().PartitionOrEmpty(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expect: expectations{
|
||||||
|
peering: &pbpeering.Peering{
|
||||||
|
ID: testBazPeerID,
|
||||||
|
Name: "baz",
|
||||||
|
// Still marked as deleting
|
||||||
|
State: pbpeering.PeeringState_DELETING,
|
||||||
|
},
|
||||||
|
// Secrets for baz should have been deleted
|
||||||
|
secrets: nil,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "cannot update peering marked for deletion",
|
name: "cannot update peering marked for deletion",
|
||||||
input: &pbpeering.PeeringWriteRequest{
|
input: &pbpeering.PeeringWriteRequest{
|
||||||
Peering: &pbpeering.Peering{
|
Peering: &pbpeering.Peering{
|
||||||
ID: testBazPeerID,
|
ID: testBazPeerID,
|
||||||
Name: "baz",
|
Name: "baz",
|
||||||
|
PeerServerAddresses: []string{"localhost:8502"},
|
||||||
|
Partition: structs.NodeEnterpriseMetaInDefaultPartition().PartitionOrEmpty(),
|
||||||
|
|
||||||
// Attempt to add metadata
|
// Attempt to add metadata
|
||||||
Meta: map[string]string{
|
Meta: map[string]string{
|
||||||
"source": "kubernetes",
|
"source": "kubernetes",
|
||||||
},
|
},
|
||||||
Partition: structs.NodeEnterpriseMetaInDefaultPartition().PartitionOrEmpty(),
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
expectErr: "cannot write to peering that is marked for deletion",
|
expect: expectations{
|
||||||
|
err: "cannot write to peering that is marked for deletion",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "cannot create peering marked for deletion",
|
name: "cannot create peering marked for deletion",
|
||||||
input: &pbpeering.PeeringWriteRequest{
|
input: &pbpeering.PeeringWriteRequest{
|
||||||
Peering: &pbpeering.Peering{
|
Peering: &pbpeering.Peering{
|
||||||
ID: testFooPeerID,
|
ID: testFooPeerID,
|
||||||
Name: "foo",
|
Name: "foo",
|
||||||
DeletedAt: structs.TimeToProto(time.Now()),
|
PeerServerAddresses: []string{"localhost:8502"},
|
||||||
Partition: structs.NodeEnterpriseMetaInDefaultPartition().PartitionOrEmpty(),
|
State: pbpeering.PeeringState_DELETING,
|
||||||
|
DeletedAt: structs.TimeToProto(time.Now()),
|
||||||
|
Partition: structs.NodeEnterpriseMetaInDefaultPartition().PartitionOrEmpty(),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
expectErr: "cannot create a new peering marked for deletion",
|
expect: expectations{
|
||||||
|
err: "cannot create a new peering marked for deletion",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
for _, tc := range tcs {
|
for _, tc := range tcs {
|
||||||
|
@ -1246,6 +1441,7 @@ func TestStore_PeeringDelete(t *testing.T) {
|
||||||
Peering: &pbpeering.Peering{
|
Peering: &pbpeering.Peering{
|
||||||
ID: testFooPeerID,
|
ID: testFooPeerID,
|
||||||
Name: "foo",
|
Name: "foo",
|
||||||
|
State: pbpeering.PeeringState_DELETING,
|
||||||
DeletedAt: structs.TimeToProto(time.Now()),
|
DeletedAt: structs.TimeToProto(time.Now()),
|
||||||
},
|
},
|
||||||
}))
|
}))
|
||||||
|
@ -1759,6 +1955,7 @@ func TestStateStore_PeeringsForService(t *testing.T) {
|
||||||
copied := pbpeering.Peering{
|
copied := pbpeering.Peering{
|
||||||
ID: tp.peering.ID,
|
ID: tp.peering.ID,
|
||||||
Name: tp.peering.Name,
|
Name: tp.peering.Name,
|
||||||
|
State: pbpeering.PeeringState_DELETING,
|
||||||
DeletedAt: structs.TimeToProto(time.Now()),
|
DeletedAt: structs.TimeToProto(time.Now()),
|
||||||
}
|
}
|
||||||
require.NoError(t, s.PeeringWrite(lastIdx, &pbpeering.PeeringWriteRequest{Peering: &copied}))
|
require.NoError(t, s.PeeringWrite(lastIdx, &pbpeering.PeeringWriteRequest{Peering: &copied}))
|
||||||
|
@ -2201,6 +2398,7 @@ func TestStore_TrustBundleListByService(t *testing.T) {
|
||||||
Peering: &pbpeering.Peering{
|
Peering: &pbpeering.Peering{
|
||||||
ID: peerID1,
|
ID: peerID1,
|
||||||
Name: "peer1",
|
Name: "peer1",
|
||||||
|
State: pbpeering.PeeringState_DELETING,
|
||||||
DeletedAt: structs.TimeToProto(time.Now()),
|
DeletedAt: structs.TimeToProto(time.Now()),
|
||||||
},
|
},
|
||||||
}))
|
}))
|
||||||
|
|
|
@ -41,8 +41,8 @@ var Gauges = []prometheus.GaugeDefinition{
|
||||||
Help: "Measures the current number of server agents registered with Consul. It is only emitted by Consul servers. Added in v1.9.6.",
|
Help: "Measures the current number of server agents registered with Consul. It is only emitted by Consul servers. Added in v1.9.6.",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: []string{"consul", "kv", "entries"},
|
Name: []string{"consul", "state", "kv_entries"},
|
||||||
Help: "Measures the current number of server agents registered with Consul. It is only emitted by Consul servers. Added in v1.10.3.",
|
Help: "Measures the current number of entries in the Consul KV store. It is only emitted by Consul servers. Added in v1.10.3.",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: []string{"consul", "state", "connect_instances"},
|
Name: []string{"consul", "state", "connect_instances"},
|
||||||
|
|
|
@ -26,11 +26,12 @@ const (
|
||||||
|
|
||||||
type Server struct {
|
type Server struct {
|
||||||
Config
|
Config
|
||||||
|
|
||||||
|
Tracker *Tracker
|
||||||
}
|
}
|
||||||
|
|
||||||
type Config struct {
|
type Config struct {
|
||||||
Backend Backend
|
Backend Backend
|
||||||
Tracker *Tracker
|
|
||||||
GetStore func() StateStore
|
GetStore func() StateStore
|
||||||
Logger hclog.Logger
|
Logger hclog.Logger
|
||||||
ForwardRPC func(structs.RPCInfo, func(*grpc.ClientConn) error) (bool, error)
|
ForwardRPC func(structs.RPCInfo, func(*grpc.ClientConn) error) (bool, error)
|
||||||
|
@ -42,8 +43,8 @@ type Config struct {
|
||||||
// outgoingHeartbeatInterval is how often we send a heartbeat.
|
// outgoingHeartbeatInterval is how often we send a heartbeat.
|
||||||
outgoingHeartbeatInterval time.Duration
|
outgoingHeartbeatInterval time.Duration
|
||||||
|
|
||||||
// IncomingHeartbeatTimeout is how long we'll wait between receiving heartbeats before we close the connection.
|
// incomingHeartbeatTimeout is how long we'll wait between receiving heartbeats before we close the connection.
|
||||||
IncomingHeartbeatTimeout time.Duration
|
incomingHeartbeatTimeout time.Duration
|
||||||
}
|
}
|
||||||
|
|
||||||
//go:generate mockery --name ACLResolver --inpackage
|
//go:generate mockery --name ACLResolver --inpackage
|
||||||
|
@ -53,7 +54,6 @@ type ACLResolver interface {
|
||||||
|
|
||||||
func NewServer(cfg Config) *Server {
|
func NewServer(cfg Config) *Server {
|
||||||
requireNotNil(cfg.Backend, "Backend")
|
requireNotNil(cfg.Backend, "Backend")
|
||||||
requireNotNil(cfg.Tracker, "Tracker")
|
|
||||||
requireNotNil(cfg.GetStore, "GetStore")
|
requireNotNil(cfg.GetStore, "GetStore")
|
||||||
requireNotNil(cfg.Logger, "Logger")
|
requireNotNil(cfg.Logger, "Logger")
|
||||||
// requireNotNil(cfg.ACLResolver, "ACLResolver") // TODO(peering): reenable check when ACLs are required
|
// requireNotNil(cfg.ACLResolver, "ACLResolver") // TODO(peering): reenable check when ACLs are required
|
||||||
|
@ -63,11 +63,12 @@ func NewServer(cfg Config) *Server {
|
||||||
if cfg.outgoingHeartbeatInterval == 0 {
|
if cfg.outgoingHeartbeatInterval == 0 {
|
||||||
cfg.outgoingHeartbeatInterval = defaultOutgoingHeartbeatInterval
|
cfg.outgoingHeartbeatInterval = defaultOutgoingHeartbeatInterval
|
||||||
}
|
}
|
||||||
if cfg.IncomingHeartbeatTimeout == 0 {
|
if cfg.incomingHeartbeatTimeout == 0 {
|
||||||
cfg.IncomingHeartbeatTimeout = defaultIncomingHeartbeatTimeout
|
cfg.incomingHeartbeatTimeout = defaultIncomingHeartbeatTimeout
|
||||||
}
|
}
|
||||||
return &Server{
|
return &Server{
|
||||||
Config: cfg,
|
Config: cfg,
|
||||||
|
Tracker: NewTracker(cfg.incomingHeartbeatTimeout),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -406,7 +406,7 @@ func (s *Server) realHandleStream(streamReq HandleStreamRequest) error {
|
||||||
|
|
||||||
// incomingHeartbeatCtx will complete if incoming heartbeats time out.
|
// incomingHeartbeatCtx will complete if incoming heartbeats time out.
|
||||||
incomingHeartbeatCtx, incomingHeartbeatCtxCancel :=
|
incomingHeartbeatCtx, incomingHeartbeatCtxCancel :=
|
||||||
context.WithTimeout(context.Background(), s.IncomingHeartbeatTimeout)
|
context.WithTimeout(context.Background(), s.incomingHeartbeatTimeout)
|
||||||
// NOTE: It's important that we wrap the call to cancel in a wrapper func because during the loop we're
|
// NOTE: It's important that we wrap the call to cancel in a wrapper func because during the loop we're
|
||||||
// re-assigning the value of incomingHeartbeatCtxCancel and we want the defer to run on the last assigned
|
// re-assigning the value of incomingHeartbeatCtxCancel and we want the defer to run on the last assigned
|
||||||
// value, not the current value.
|
// value, not the current value.
|
||||||
|
@ -575,6 +575,7 @@ func (s *Server) realHandleStream(streamReq HandleStreamRequest) error {
|
||||||
status.TrackRecvResourceSuccess()
|
status.TrackRecvResourceSuccess()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// We are replying ACK or NACK depending on whether we successfully processed the response.
|
||||||
if err := streamSend(reply); err != nil {
|
if err := streamSend(reply); err != nil {
|
||||||
return fmt.Errorf("failed to send to stream: %v", err)
|
return fmt.Errorf("failed to send to stream: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -605,7 +606,7 @@ func (s *Server) realHandleStream(streamReq HandleStreamRequest) error {
|
||||||
// They just can't trace the execution properly for some reason (possibly golang/go#29587).
|
// They just can't trace the execution properly for some reason (possibly golang/go#29587).
|
||||||
//nolint:govet
|
//nolint:govet
|
||||||
incomingHeartbeatCtx, incomingHeartbeatCtxCancel =
|
incomingHeartbeatCtx, incomingHeartbeatCtxCancel =
|
||||||
context.WithTimeout(context.Background(), s.IncomingHeartbeatTimeout)
|
context.WithTimeout(context.Background(), s.incomingHeartbeatTimeout)
|
||||||
}
|
}
|
||||||
|
|
||||||
case update := <-subCh:
|
case update := <-subCh:
|
||||||
|
@ -642,7 +643,6 @@ func (s *Server) realHandleStream(streamReq HandleStreamRequest) error {
|
||||||
if err := streamSend(replResp); err != nil {
|
if err := streamSend(replResp); err != nil {
|
||||||
return fmt.Errorf("failed to push data for %q: %w", update.CorrelationID, err)
|
return fmt.Errorf("failed to push data for %q: %w", update.CorrelationID, err)
|
||||||
}
|
}
|
||||||
status.TrackSendSuccess()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -499,9 +499,8 @@ func TestStreamResources_Server_Terminate(t *testing.T) {
|
||||||
base: time.Date(2000, time.January, 1, 0, 0, 0, 0, time.UTC),
|
base: time.Date(2000, time.January, 1, 0, 0, 0, 0, time.UTC),
|
||||||
}
|
}
|
||||||
|
|
||||||
srv, store := newTestServer(t, func(c *Config) {
|
srv, store := newTestServer(t, nil)
|
||||||
c.Tracker.SetClock(it.Now)
|
srv.Tracker.setClock(it.Now)
|
||||||
})
|
|
||||||
|
|
||||||
p := writePeeringToBeDialed(t, store, 1, "my-peer")
|
p := writePeeringToBeDialed(t, store, 1, "my-peer")
|
||||||
require.Empty(t, p.PeerID, "should be empty if being dialed")
|
require.Empty(t, p.PeerID, "should be empty if being dialed")
|
||||||
|
@ -552,9 +551,8 @@ func TestStreamResources_Server_StreamTracker(t *testing.T) {
|
||||||
base: time.Date(2000, time.January, 1, 0, 0, 0, 0, time.UTC),
|
base: time.Date(2000, time.January, 1, 0, 0, 0, 0, time.UTC),
|
||||||
}
|
}
|
||||||
|
|
||||||
srv, store := newTestServer(t, func(c *Config) {
|
srv, store := newTestServer(t, nil)
|
||||||
c.Tracker.SetClock(it.Now)
|
srv.Tracker.setClock(it.Now)
|
||||||
})
|
|
||||||
|
|
||||||
// Set the initial roots and CA configuration.
|
// Set the initial roots and CA configuration.
|
||||||
_, rootA := writeInitialRootsAndCA(t, store)
|
_, rootA := writeInitialRootsAndCA(t, store)
|
||||||
|
@ -572,7 +570,7 @@ func TestStreamResources_Server_StreamTracker(t *testing.T) {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
var lastSendAck, lastSendSuccess time.Time
|
var lastSendAck time.Time
|
||||||
|
|
||||||
testutil.RunStep(t, "ack tracked as success", func(t *testing.T) {
|
testutil.RunStep(t, "ack tracked as success", func(t *testing.T) {
|
||||||
ack := &pbpeerstream.ReplicationMessage{
|
ack := &pbpeerstream.ReplicationMessage{
|
||||||
|
@ -587,16 +585,13 @@ func TestStreamResources_Server_StreamTracker(t *testing.T) {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
lastSendAck = time.Date(2000, time.January, 1, 0, 0, 2, 0, time.UTC)
|
lastSendAck = it.FutureNow(1)
|
||||||
lastSendSuccess = time.Date(2000, time.January, 1, 0, 0, 3, 0, time.UTC)
|
|
||||||
err := client.Send(ack)
|
err := client.Send(ack)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
expect := Status{
|
expect := Status{
|
||||||
Connected: true,
|
Connected: true,
|
||||||
LastAck: lastSendAck,
|
LastAck: lastSendAck,
|
||||||
heartbeatTimeout: defaultIncomingHeartbeatTimeout,
|
|
||||||
LastSendSuccess: lastSendSuccess,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
retry.Run(t, func(r *retry.R) {
|
retry.Run(t, func(r *retry.R) {
|
||||||
|
@ -624,20 +619,17 @@ func TestStreamResources_Server_StreamTracker(t *testing.T) {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
lastSendAck = time.Date(2000, time.January, 1, 0, 0, 4, 0, time.UTC)
|
lastNack = it.FutureNow(1)
|
||||||
lastNack = time.Date(2000, time.January, 1, 0, 0, 5, 0, time.UTC)
|
|
||||||
err := client.Send(nack)
|
err := client.Send(nack)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
lastNackMsg = "client peer was unable to apply resource: bad bad not good"
|
lastNackMsg = "client peer was unable to apply resource: bad bad not good"
|
||||||
|
|
||||||
expect := Status{
|
expect := Status{
|
||||||
Connected: true,
|
Connected: true,
|
||||||
LastAck: lastSendAck,
|
LastAck: lastSendAck,
|
||||||
LastNack: lastNack,
|
LastNack: lastNack,
|
||||||
LastNackMessage: lastNackMsg,
|
LastNackMessage: lastNackMsg,
|
||||||
heartbeatTimeout: defaultIncomingHeartbeatTimeout,
|
|
||||||
LastSendSuccess: lastSendSuccess,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
retry.Run(t, func(r *retry.R) {
|
retry.Run(t, func(r *retry.R) {
|
||||||
|
@ -707,8 +699,6 @@ func TestStreamResources_Server_StreamTracker(t *testing.T) {
|
||||||
ImportedServices: map[string]struct{}{
|
ImportedServices: map[string]struct{}{
|
||||||
api.String(): {},
|
api.String(): {},
|
||||||
},
|
},
|
||||||
heartbeatTimeout: defaultIncomingHeartbeatTimeout,
|
|
||||||
LastSendSuccess: lastSendSuccess,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
retry.Run(t, func(r *retry.R) {
|
retry.Run(t, func(r *retry.R) {
|
||||||
|
@ -770,8 +760,6 @@ func TestStreamResources_Server_StreamTracker(t *testing.T) {
|
||||||
ImportedServices: map[string]struct{}{
|
ImportedServices: map[string]struct{}{
|
||||||
api.String(): {},
|
api.String(): {},
|
||||||
},
|
},
|
||||||
heartbeatTimeout: defaultIncomingHeartbeatTimeout,
|
|
||||||
LastSendSuccess: lastSendSuccess,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
retry.Run(t, func(r *retry.R) {
|
retry.Run(t, func(r *retry.R) {
|
||||||
|
@ -805,8 +793,6 @@ func TestStreamResources_Server_StreamTracker(t *testing.T) {
|
||||||
ImportedServices: map[string]struct{}{
|
ImportedServices: map[string]struct{}{
|
||||||
api.String(): {},
|
api.String(): {},
|
||||||
},
|
},
|
||||||
heartbeatTimeout: defaultIncomingHeartbeatTimeout,
|
|
||||||
LastSendSuccess: lastSendSuccess,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
retry.Run(t, func(r *retry.R) {
|
retry.Run(t, func(r *retry.R) {
|
||||||
|
@ -839,8 +825,6 @@ func TestStreamResources_Server_StreamTracker(t *testing.T) {
|
||||||
ImportedServices: map[string]struct{}{
|
ImportedServices: map[string]struct{}{
|
||||||
api.String(): {},
|
api.String(): {},
|
||||||
},
|
},
|
||||||
heartbeatTimeout: defaultIncomingHeartbeatTimeout,
|
|
||||||
LastSendSuccess: lastSendSuccess,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
retry.Run(t, func(r *retry.R) {
|
retry.Run(t, func(r *retry.R) {
|
||||||
|
@ -1142,9 +1126,9 @@ func TestStreamResources_Server_DisconnectsOnHeartbeatTimeout(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
srv, store := newTestServer(t, func(c *Config) {
|
srv, store := newTestServer(t, func(c *Config) {
|
||||||
c.Tracker.SetClock(it.Now)
|
c.incomingHeartbeatTimeout = 5 * time.Millisecond
|
||||||
c.IncomingHeartbeatTimeout = 5 * time.Millisecond
|
|
||||||
})
|
})
|
||||||
|
srv.Tracker.setClock(it.Now)
|
||||||
|
|
||||||
p := writePeeringToBeDialed(t, store, 1, "my-peer")
|
p := writePeeringToBeDialed(t, store, 1, "my-peer")
|
||||||
require.Empty(t, p.PeerID, "should be empty if being dialed")
|
require.Empty(t, p.PeerID, "should be empty if being dialed")
|
||||||
|
@ -1190,9 +1174,9 @@ func TestStreamResources_Server_SendsHeartbeats(t *testing.T) {
|
||||||
outgoingHeartbeatInterval := 5 * time.Millisecond
|
outgoingHeartbeatInterval := 5 * time.Millisecond
|
||||||
|
|
||||||
srv, store := newTestServer(t, func(c *Config) {
|
srv, store := newTestServer(t, func(c *Config) {
|
||||||
c.Tracker.SetClock(it.Now)
|
|
||||||
c.outgoingHeartbeatInterval = outgoingHeartbeatInterval
|
c.outgoingHeartbeatInterval = outgoingHeartbeatInterval
|
||||||
})
|
})
|
||||||
|
srv.Tracker.setClock(it.Now)
|
||||||
|
|
||||||
p := writePeeringToBeDialed(t, store, 1, "my-peer")
|
p := writePeeringToBeDialed(t, store, 1, "my-peer")
|
||||||
require.Empty(t, p.PeerID, "should be empty if being dialed")
|
require.Empty(t, p.PeerID, "should be empty if being dialed")
|
||||||
|
@ -1249,9 +1233,9 @@ func TestStreamResources_Server_KeepsConnectionOpenWithHeartbeat(t *testing.T) {
|
||||||
incomingHeartbeatTimeout := 10 * time.Millisecond
|
incomingHeartbeatTimeout := 10 * time.Millisecond
|
||||||
|
|
||||||
srv, store := newTestServer(t, func(c *Config) {
|
srv, store := newTestServer(t, func(c *Config) {
|
||||||
c.Tracker.SetClock(it.Now)
|
c.incomingHeartbeatTimeout = incomingHeartbeatTimeout
|
||||||
c.IncomingHeartbeatTimeout = incomingHeartbeatTimeout
|
|
||||||
})
|
})
|
||||||
|
srv.Tracker.setClock(it.Now)
|
||||||
|
|
||||||
p := writePeeringToBeDialed(t, store, 1, "my-peer")
|
p := writePeeringToBeDialed(t, store, 1, "my-peer")
|
||||||
require.Empty(t, p.PeerID, "should be empty if being dialed")
|
require.Empty(t, p.PeerID, "should be empty if being dialed")
|
||||||
|
@ -2760,7 +2744,6 @@ func newTestServer(t *testing.T, configFn func(c *Config)) (*testServer, *state.
|
||||||
store: store,
|
store: store,
|
||||||
pub: publisher,
|
pub: publisher,
|
||||||
},
|
},
|
||||||
Tracker: NewTracker(),
|
|
||||||
GetStore: func() StateStore { return store },
|
GetStore: func() StateStore { return store },
|
||||||
Logger: testutil.Logger(t),
|
Logger: testutil.Logger(t),
|
||||||
Datacenter: "dc1",
|
Datacenter: "dc1",
|
||||||
|
|
|
@ -14,20 +14,27 @@ type Tracker struct {
|
||||||
mu sync.RWMutex
|
mu sync.RWMutex
|
||||||
streams map[string]*MutableStatus
|
streams map[string]*MutableStatus
|
||||||
|
|
||||||
|
// heartbeatTimeout is the max duration a connection is allowed to be
|
||||||
|
// disconnected before the stream health is reported as non-healthy
|
||||||
|
heartbeatTimeout time.Duration
|
||||||
|
|
||||||
// timeNow is a shim for testing.
|
// timeNow is a shim for testing.
|
||||||
timeNow func() time.Time
|
timeNow func() time.Time
|
||||||
|
|
||||||
heartbeatTimeout time.Duration
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewTracker() *Tracker {
|
func NewTracker(heartbeatTimeout time.Duration) *Tracker {
|
||||||
|
if heartbeatTimeout == 0 {
|
||||||
|
heartbeatTimeout = defaultIncomingHeartbeatTimeout
|
||||||
|
}
|
||||||
return &Tracker{
|
return &Tracker{
|
||||||
streams: make(map[string]*MutableStatus),
|
streams: make(map[string]*MutableStatus),
|
||||||
timeNow: time.Now,
|
timeNow: time.Now,
|
||||||
|
heartbeatTimeout: heartbeatTimeout,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Tracker) SetClock(clock func() time.Time) {
|
// setClock is used for debugging purposes only.
|
||||||
|
func (t *Tracker) setClock(clock func() time.Time) {
|
||||||
if clock == nil {
|
if clock == nil {
|
||||||
t.timeNow = time.Now
|
t.timeNow = time.Now
|
||||||
} else {
|
} else {
|
||||||
|
@ -35,12 +42,6 @@ func (t *Tracker) SetClock(clock func() time.Time) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Tracker) SetHeartbeatTimeout(heartbeatTimeout time.Duration) {
|
|
||||||
t.mu.Lock()
|
|
||||||
defer t.mu.Unlock()
|
|
||||||
t.heartbeatTimeout = heartbeatTimeout
|
|
||||||
}
|
|
||||||
|
|
||||||
// Register a stream for a given peer but do not mark it as connected.
|
// Register a stream for a given peer but do not mark it as connected.
|
||||||
func (t *Tracker) Register(id string) (*MutableStatus, error) {
|
func (t *Tracker) Register(id string) (*MutableStatus, error) {
|
||||||
t.mu.Lock()
|
t.mu.Lock()
|
||||||
|
@ -52,7 +53,7 @@ func (t *Tracker) Register(id string) (*MutableStatus, error) {
|
||||||
func (t *Tracker) registerLocked(id string, initAsConnected bool) (*MutableStatus, bool, error) {
|
func (t *Tracker) registerLocked(id string, initAsConnected bool) (*MutableStatus, bool, error) {
|
||||||
status, ok := t.streams[id]
|
status, ok := t.streams[id]
|
||||||
if !ok {
|
if !ok {
|
||||||
status = newMutableStatus(t.timeNow, t.heartbeatTimeout, initAsConnected)
|
status = newMutableStatus(t.timeNow, initAsConnected)
|
||||||
t.streams[id] = status
|
t.streams[id] = status
|
||||||
return status, true, nil
|
return status, true, nil
|
||||||
}
|
}
|
||||||
|
@ -136,6 +137,39 @@ func (t *Tracker) DeleteStatus(id string) {
|
||||||
delete(t.streams, id)
|
delete(t.streams, id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsHealthy is a calculates the health of a peering status.
|
||||||
|
// We define a peering as unhealthy if its status has been in the following
|
||||||
|
// states for longer than the configured incomingHeartbeatTimeout.
|
||||||
|
// - If it is disconnected
|
||||||
|
// - If the last received Nack is newer than last received Ack
|
||||||
|
// - If the last received error is newer than last received success
|
||||||
|
//
|
||||||
|
// If none of these conditions apply, we call the peering healthy.
|
||||||
|
func (t *Tracker) IsHealthy(s Status) bool {
|
||||||
|
// If stream is in a disconnected state for longer than the configured
|
||||||
|
// heartbeat timeout, report as unhealthy.
|
||||||
|
if !s.DisconnectTime.IsZero() &&
|
||||||
|
t.timeNow().Sub(s.DisconnectTime) > t.heartbeatTimeout {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// If last Nack is after last Ack, it means the peer is unable to
|
||||||
|
// handle our replication message.
|
||||||
|
if s.LastNack.After(s.LastAck) &&
|
||||||
|
t.timeNow().Sub(s.LastAck) > t.heartbeatTimeout {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// If last recv error is newer than last recv success, we were unable
|
||||||
|
// to handle the peer's replication message.
|
||||||
|
if s.LastRecvError.After(s.LastRecvResourceSuccess) &&
|
||||||
|
t.timeNow().Sub(s.LastRecvError) > t.heartbeatTimeout {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
type MutableStatus struct {
|
type MutableStatus struct {
|
||||||
mu sync.RWMutex
|
mu sync.RWMutex
|
||||||
|
|
||||||
|
@ -152,8 +186,6 @@ type MutableStatus struct {
|
||||||
// Status contains information about the replication stream to a peer cluster.
|
// Status contains information about the replication stream to a peer cluster.
|
||||||
// TODO(peering): There's a lot of fields here...
|
// TODO(peering): There's a lot of fields here...
|
||||||
type Status struct {
|
type Status struct {
|
||||||
heartbeatTimeout time.Duration
|
|
||||||
|
|
||||||
// Connected is true when there is an open stream for the peer.
|
// Connected is true when there is an open stream for the peer.
|
||||||
Connected bool
|
Connected bool
|
||||||
|
|
||||||
|
@ -182,9 +214,6 @@ type Status struct {
|
||||||
// LastSendErrorMessage tracks the last error message when sending into the stream.
|
// LastSendErrorMessage tracks the last error message when sending into the stream.
|
||||||
LastSendErrorMessage string
|
LastSendErrorMessage string
|
||||||
|
|
||||||
// LastSendSuccess tracks the time of the last success response sent into the stream.
|
|
||||||
LastSendSuccess time.Time
|
|
||||||
|
|
||||||
// LastRecvHeartbeat tracks when we last received a heartbeat from our peer.
|
// LastRecvHeartbeat tracks when we last received a heartbeat from our peer.
|
||||||
LastRecvHeartbeat time.Time
|
LastRecvHeartbeat time.Time
|
||||||
|
|
||||||
|
@ -214,40 +243,11 @@ func (s *Status) GetExportedServicesCount() uint64 {
|
||||||
return uint64(len(s.ExportedServices))
|
return uint64(len(s.ExportedServices))
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsHealthy is a convenience func that returns true/ false for a peering status.
|
func newMutableStatus(now func() time.Time, connected bool) *MutableStatus {
|
||||||
// We define a peering as unhealthy if its status satisfies one of the following:
|
|
||||||
// - If heartbeat hasn't been received within the IncomingHeartbeatTimeout
|
|
||||||
// - If the last sent error is newer than last sent success
|
|
||||||
// - If the last received error is newer than last received success
|
|
||||||
// If none of these conditions apply, we call the peering healthy.
|
|
||||||
func (s *Status) IsHealthy() bool {
|
|
||||||
if time.Now().Sub(s.LastRecvHeartbeat) > s.heartbeatTimeout {
|
|
||||||
// 1. If heartbeat hasn't been received for a while - report unhealthy
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
if s.LastSendError.After(s.LastSendSuccess) {
|
|
||||||
// 2. If last sent error is newer than last sent success - report unhealthy
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
if s.LastRecvError.After(s.LastRecvResourceSuccess) {
|
|
||||||
// 3. If last recv error is newer than last recv success - report unhealthy
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
func newMutableStatus(now func() time.Time, heartbeatTimeout time.Duration, connected bool) *MutableStatus {
|
|
||||||
if heartbeatTimeout.Microseconds() == 0 {
|
|
||||||
heartbeatTimeout = defaultIncomingHeartbeatTimeout
|
|
||||||
}
|
|
||||||
return &MutableStatus{
|
return &MutableStatus{
|
||||||
Status: Status{
|
Status: Status{
|
||||||
Connected: connected,
|
Connected: connected,
|
||||||
heartbeatTimeout: heartbeatTimeout,
|
NeverConnected: !connected,
|
||||||
NeverConnected: !connected,
|
|
||||||
},
|
},
|
||||||
timeNow: now,
|
timeNow: now,
|
||||||
doneCh: make(chan struct{}),
|
doneCh: make(chan struct{}),
|
||||||
|
@ -271,12 +271,6 @@ func (s *MutableStatus) TrackSendError(error string) {
|
||||||
s.mu.Unlock()
|
s.mu.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *MutableStatus) TrackSendSuccess() {
|
|
||||||
s.mu.Lock()
|
|
||||||
s.LastSendSuccess = s.timeNow().UTC()
|
|
||||||
s.mu.Unlock()
|
|
||||||
}
|
|
||||||
|
|
||||||
// TrackRecvResourceSuccess tracks receiving a replicated resource.
|
// TrackRecvResourceSuccess tracks receiving a replicated resource.
|
||||||
func (s *MutableStatus) TrackRecvResourceSuccess() {
|
func (s *MutableStatus) TrackRecvResourceSuccess() {
|
||||||
s.mu.Lock()
|
s.mu.Lock()
|
||||||
|
|
|
@ -5,6 +5,7 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
"github.com/hashicorp/consul/sdk/testutil"
|
"github.com/hashicorp/consul/sdk/testutil"
|
||||||
|
@ -14,95 +15,107 @@ const (
|
||||||
aPeerID = "63b60245-c475-426b-b314-4588d210859d"
|
aPeerID = "63b60245-c475-426b-b314-4588d210859d"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestStatus_IsHealthy(t *testing.T) {
|
func TestTracker_IsHealthy(t *testing.T) {
|
||||||
type testcase struct {
|
type testcase struct {
|
||||||
name string
|
name string
|
||||||
dontConnect bool
|
tracker *Tracker
|
||||||
modifierFunc func(status *MutableStatus)
|
modifierFunc func(status *MutableStatus)
|
||||||
expectedVal bool
|
expectedVal bool
|
||||||
heartbeatTimeout time.Duration
|
|
||||||
}
|
}
|
||||||
|
|
||||||
tcs := []testcase{
|
tcs := []testcase{
|
||||||
{
|
{
|
||||||
name: "never connected, unhealthy",
|
name: "disconnect time within timeout",
|
||||||
expectedVal: false,
|
tracker: NewTracker(defaultIncomingHeartbeatTimeout),
|
||||||
dontConnect: true,
|
expectedVal: true,
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "no heartbeat, unhealthy",
|
|
||||||
expectedVal: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "heartbeat is not received, unhealthy",
|
|
||||||
expectedVal: false,
|
|
||||||
modifierFunc: func(status *MutableStatus) {
|
modifierFunc: func(status *MutableStatus) {
|
||||||
// set heartbeat
|
status.DisconnectTime = time.Now()
|
||||||
status.LastRecvHeartbeat = time.Now().Add(-1 * time.Second)
|
|
||||||
},
|
|
||||||
heartbeatTimeout: 1 * time.Second,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "send error before send success",
|
|
||||||
expectedVal: false,
|
|
||||||
modifierFunc: func(status *MutableStatus) {
|
|
||||||
// set heartbeat
|
|
||||||
status.LastRecvHeartbeat = time.Now()
|
|
||||||
|
|
||||||
status.LastSendSuccess = time.Now()
|
|
||||||
status.LastSendError = time.Now()
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "received error before received success",
|
name: "disconnect time past timeout",
|
||||||
|
tracker: NewTracker(1 * time.Millisecond),
|
||||||
expectedVal: false,
|
expectedVal: false,
|
||||||
modifierFunc: func(status *MutableStatus) {
|
modifierFunc: func(status *MutableStatus) {
|
||||||
// set heartbeat
|
status.DisconnectTime = time.Now().Add(-1 * time.Minute)
|
||||||
status.LastRecvHeartbeat = time.Now()
|
},
|
||||||
|
},
|
||||||
status.LastRecvResourceSuccess = time.Now()
|
{
|
||||||
status.LastRecvError = time.Now()
|
name: "receive error before receive success within timeout",
|
||||||
|
tracker: NewTracker(defaultIncomingHeartbeatTimeout),
|
||||||
|
expectedVal: true,
|
||||||
|
modifierFunc: func(status *MutableStatus) {
|
||||||
|
now := time.Now()
|
||||||
|
status.LastRecvResourceSuccess = now
|
||||||
|
status.LastRecvError = now.Add(1 * time.Second)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "receive error before receive success within timeout",
|
||||||
|
tracker: NewTracker(defaultIncomingHeartbeatTimeout),
|
||||||
|
expectedVal: true,
|
||||||
|
modifierFunc: func(status *MutableStatus) {
|
||||||
|
now := time.Now()
|
||||||
|
status.LastRecvResourceSuccess = now
|
||||||
|
status.LastRecvError = now.Add(1 * time.Second)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "receive error before receive success past timeout",
|
||||||
|
tracker: NewTracker(1 * time.Millisecond),
|
||||||
|
expectedVal: false,
|
||||||
|
modifierFunc: func(status *MutableStatus) {
|
||||||
|
now := time.Now().Add(-2 * time.Second)
|
||||||
|
status.LastRecvResourceSuccess = now
|
||||||
|
status.LastRecvError = now.Add(1 * time.Second)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "nack before ack within timeout",
|
||||||
|
tracker: NewTracker(defaultIncomingHeartbeatTimeout),
|
||||||
|
expectedVal: true,
|
||||||
|
modifierFunc: func(status *MutableStatus) {
|
||||||
|
now := time.Now()
|
||||||
|
status.LastAck = now
|
||||||
|
status.LastNack = now.Add(1 * time.Second)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "nack before ack past timeout",
|
||||||
|
tracker: NewTracker(1 * time.Millisecond),
|
||||||
|
expectedVal: false,
|
||||||
|
modifierFunc: func(status *MutableStatus) {
|
||||||
|
now := time.Now().Add(-2 * time.Second)
|
||||||
|
status.LastAck = now
|
||||||
|
status.LastNack = now.Add(1 * time.Second)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "healthy",
|
name: "healthy",
|
||||||
|
tracker: NewTracker(defaultIncomingHeartbeatTimeout),
|
||||||
expectedVal: true,
|
expectedVal: true,
|
||||||
modifierFunc: func(status *MutableStatus) {
|
|
||||||
// set heartbeat
|
|
||||||
status.LastRecvHeartbeat = time.Now()
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, tc := range tcs {
|
for _, tc := range tcs {
|
||||||
t.Run(tc.name, func(t *testing.T) {
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
tracker := NewTracker()
|
tracker := tc.tracker
|
||||||
if tc.heartbeatTimeout.Microseconds() != 0 {
|
|
||||||
tracker.SetHeartbeatTimeout(tc.heartbeatTimeout)
|
st, err := tracker.Connected(aPeerID)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.True(t, st.Connected)
|
||||||
|
|
||||||
|
if tc.modifierFunc != nil {
|
||||||
|
tc.modifierFunc(st)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !tc.dontConnect {
|
assert.Equal(t, tc.expectedVal, tracker.IsHealthy(st.GetStatus()))
|
||||||
st, err := tracker.Connected(aPeerID)
|
|
||||||
require.NoError(t, err)
|
|
||||||
require.True(t, st.Connected)
|
|
||||||
|
|
||||||
if tc.modifierFunc != nil {
|
|
||||||
tc.modifierFunc(st)
|
|
||||||
}
|
|
||||||
|
|
||||||
require.Equal(t, tc.expectedVal, st.IsHealthy())
|
|
||||||
|
|
||||||
} else {
|
|
||||||
st, found := tracker.StreamStatus(aPeerID)
|
|
||||||
require.False(t, found)
|
|
||||||
require.Equal(t, tc.expectedVal, st.IsHealthy())
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestTracker_EnsureConnectedDisconnected(t *testing.T) {
|
func TestTracker_EnsureConnectedDisconnected(t *testing.T) {
|
||||||
tracker := NewTracker()
|
tracker := NewTracker(defaultIncomingHeartbeatTimeout)
|
||||||
peerID := "63b60245-c475-426b-b314-4588d210859d"
|
peerID := "63b60245-c475-426b-b314-4588d210859d"
|
||||||
|
|
||||||
it := incrementalTime{
|
it := incrementalTime{
|
||||||
|
@ -120,8 +133,7 @@ func TestTracker_EnsureConnectedDisconnected(t *testing.T) {
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
expect := Status{
|
expect := Status{
|
||||||
Connected: true,
|
Connected: true,
|
||||||
heartbeatTimeout: defaultIncomingHeartbeatTimeout,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
status, ok := tracker.StreamStatus(peerID)
|
status, ok := tracker.StreamStatus(peerID)
|
||||||
|
@ -147,9 +159,8 @@ func TestTracker_EnsureConnectedDisconnected(t *testing.T) {
|
||||||
|
|
||||||
lastSuccess = it.base.Add(time.Duration(sequence) * time.Second).UTC()
|
lastSuccess = it.base.Add(time.Duration(sequence) * time.Second).UTC()
|
||||||
expect := Status{
|
expect := Status{
|
||||||
Connected: true,
|
Connected: true,
|
||||||
LastAck: lastSuccess,
|
LastAck: lastSuccess,
|
||||||
heartbeatTimeout: defaultIncomingHeartbeatTimeout,
|
|
||||||
}
|
}
|
||||||
require.Equal(t, expect, status)
|
require.Equal(t, expect, status)
|
||||||
})
|
})
|
||||||
|
@ -159,10 +170,9 @@ func TestTracker_EnsureConnectedDisconnected(t *testing.T) {
|
||||||
sequence++
|
sequence++
|
||||||
|
|
||||||
expect := Status{
|
expect := Status{
|
||||||
Connected: false,
|
Connected: false,
|
||||||
DisconnectTime: it.base.Add(time.Duration(sequence) * time.Second).UTC(),
|
DisconnectTime: it.base.Add(time.Duration(sequence) * time.Second).UTC(),
|
||||||
LastAck: lastSuccess,
|
LastAck: lastSuccess,
|
||||||
heartbeatTimeout: defaultIncomingHeartbeatTimeout,
|
|
||||||
}
|
}
|
||||||
status, ok := tracker.StreamStatus(peerID)
|
status, ok := tracker.StreamStatus(peerID)
|
||||||
require.True(t, ok)
|
require.True(t, ok)
|
||||||
|
@ -174,9 +184,8 @@ func TestTracker_EnsureConnectedDisconnected(t *testing.T) {
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
expect := Status{
|
expect := Status{
|
||||||
Connected: true,
|
Connected: true,
|
||||||
LastAck: lastSuccess,
|
LastAck: lastSuccess,
|
||||||
heartbeatTimeout: defaultIncomingHeartbeatTimeout,
|
|
||||||
|
|
||||||
// DisconnectTime gets cleared on re-connect.
|
// DisconnectTime gets cleared on re-connect.
|
||||||
}
|
}
|
||||||
|
@ -203,7 +212,7 @@ func TestTracker_connectedStreams(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
run := func(t *testing.T, tc testCase) {
|
run := func(t *testing.T, tc testCase) {
|
||||||
tracker := NewTracker()
|
tracker := NewTracker(defaultIncomingHeartbeatTimeout)
|
||||||
if tc.setup != nil {
|
if tc.setup != nil {
|
||||||
tc.setup(t, tracker)
|
tc.setup(t, tracker)
|
||||||
}
|
}
|
||||||
|
|
|
@ -280,16 +280,6 @@ func (s *handlerConnectProxy) handleUpdate(ctx context.Context, u UpdateEvent, s
|
||||||
}
|
}
|
||||||
snap.Roots = roots
|
snap.Roots = roots
|
||||||
|
|
||||||
case strings.HasPrefix(u.CorrelationID, peerTrustBundleIDPrefix):
|
|
||||||
resp, ok := u.Result.(*pbpeering.TrustBundleReadResponse)
|
|
||||||
if !ok {
|
|
||||||
return fmt.Errorf("invalid type for response: %T", u.Result)
|
|
||||||
}
|
|
||||||
peer := strings.TrimPrefix(u.CorrelationID, peerTrustBundleIDPrefix)
|
|
||||||
if resp.Bundle != nil {
|
|
||||||
snap.ConnectProxy.UpstreamPeerTrustBundles.Set(peer, resp.Bundle)
|
|
||||||
}
|
|
||||||
|
|
||||||
case u.CorrelationID == peeringTrustBundlesWatchID:
|
case u.CorrelationID == peeringTrustBundlesWatchID:
|
||||||
resp, ok := u.Result.(*pbpeering.TrustBundleListByServiceResponse)
|
resp, ok := u.Result.(*pbpeering.TrustBundleListByServiceResponse)
|
||||||
if !ok {
|
if !ok {
|
||||||
|
@ -369,6 +359,17 @@ func (s *handlerConnectProxy) handleUpdate(ctx context.Context, u UpdateEvent, s
|
||||||
// Clean up data
|
// Clean up data
|
||||||
//
|
//
|
||||||
|
|
||||||
|
peeredChainTargets := make(map[UpstreamID]struct{})
|
||||||
|
for _, discoChain := range snap.ConnectProxy.DiscoveryChain {
|
||||||
|
for _, target := range discoChain.Targets {
|
||||||
|
if target.Peer == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
uid := NewUpstreamIDFromTargetID(target.ID)
|
||||||
|
peeredChainTargets[uid] = struct{}{}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
validPeerNames := make(map[string]struct{})
|
validPeerNames := make(map[string]struct{})
|
||||||
|
|
||||||
// Iterate through all known endpoints and remove references to upstream IDs that weren't in the update
|
// Iterate through all known endpoints and remove references to upstream IDs that weren't in the update
|
||||||
|
@ -383,6 +384,11 @@ func (s *handlerConnectProxy) handleUpdate(ctx context.Context, u UpdateEvent, s
|
||||||
validPeerNames[uid.Peer] = struct{}{}
|
validPeerNames[uid.Peer] = struct{}{}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
// Peered upstream came from a discovery chain target
|
||||||
|
if _, ok := peeredChainTargets[uid]; ok {
|
||||||
|
validPeerNames[uid.Peer] = struct{}{}
|
||||||
|
return true
|
||||||
|
}
|
||||||
snap.ConnectProxy.PeerUpstreamEndpoints.CancelWatch(uid)
|
snap.ConnectProxy.PeerUpstreamEndpoints.CancelWatch(uid)
|
||||||
return true
|
return true
|
||||||
})
|
})
|
||||||
|
@ -463,8 +469,14 @@ func (s *handlerConnectProxy) handleUpdate(ctx context.Context, u UpdateEvent, s
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if _, ok := seenUpstreams[uid]; !ok {
|
if _, ok := seenUpstreams[uid]; !ok {
|
||||||
for _, cancelFn := range targets {
|
for targetID, cancelFn := range targets {
|
||||||
cancelFn()
|
cancelFn()
|
||||||
|
|
||||||
|
targetUID := NewUpstreamIDFromTargetID(targetID)
|
||||||
|
if targetUID.Peer != "" {
|
||||||
|
snap.ConnectProxy.PeerUpstreamEndpoints.CancelWatch(targetUID)
|
||||||
|
snap.ConnectProxy.UpstreamPeerTrustBundles.CancelWatch(targetUID.Peer)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
delete(snap.ConnectProxy.WatchedUpstreams, uid)
|
delete(snap.ConnectProxy.WatchedUpstreams, uid)
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,9 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
cachetype "github.com/hashicorp/consul/agent/cache-types"
|
cachetype "github.com/hashicorp/consul/agent/cache-types"
|
||||||
|
"github.com/hashicorp/consul/agent/proxycfg/internal/watch"
|
||||||
"github.com/hashicorp/consul/agent/structs"
|
"github.com/hashicorp/consul/agent/structs"
|
||||||
|
"github.com/hashicorp/consul/proto/pbpeering"
|
||||||
)
|
)
|
||||||
|
|
||||||
type handlerIngressGateway struct {
|
type handlerIngressGateway struct {
|
||||||
|
@ -66,6 +68,9 @@ func (s *handlerIngressGateway) initialize(ctx context.Context) (ConfigSnapshot,
|
||||||
snap.IngressGateway.WatchedGateways = make(map[UpstreamID]map[string]context.CancelFunc)
|
snap.IngressGateway.WatchedGateways = make(map[UpstreamID]map[string]context.CancelFunc)
|
||||||
snap.IngressGateway.WatchedGatewayEndpoints = make(map[UpstreamID]map[string]structs.CheckServiceNodes)
|
snap.IngressGateway.WatchedGatewayEndpoints = make(map[UpstreamID]map[string]structs.CheckServiceNodes)
|
||||||
snap.IngressGateway.Listeners = make(map[IngressListenerKey]structs.IngressListener)
|
snap.IngressGateway.Listeners = make(map[IngressListenerKey]structs.IngressListener)
|
||||||
|
snap.IngressGateway.UpstreamPeerTrustBundles = watch.NewMap[string, *pbpeering.PeeringTrustBundle]()
|
||||||
|
snap.IngressGateway.PeerUpstreamEndpoints = watch.NewMap[UpstreamID, structs.CheckServiceNodes]()
|
||||||
|
snap.IngressGateway.PeerUpstreamEndpointsUseHostnames = make(map[UpstreamID]struct{})
|
||||||
return snap, nil
|
return snap, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -152,6 +157,12 @@ func (s *handlerIngressGateway) handleUpdate(ctx context.Context, u UpdateEvent,
|
||||||
delete(snap.IngressGateway.WatchedUpstreams[uid], targetID)
|
delete(snap.IngressGateway.WatchedUpstreams[uid], targetID)
|
||||||
delete(snap.IngressGateway.WatchedUpstreamEndpoints[uid], targetID)
|
delete(snap.IngressGateway.WatchedUpstreamEndpoints[uid], targetID)
|
||||||
cancelUpstreamFn()
|
cancelUpstreamFn()
|
||||||
|
|
||||||
|
targetUID := NewUpstreamIDFromTargetID(targetID)
|
||||||
|
if targetUID.Peer != "" {
|
||||||
|
snap.IngressGateway.PeerUpstreamEndpoints.CancelWatch(targetUID)
|
||||||
|
snap.IngressGateway.UpstreamPeerTrustBundles.CancelWatch(targetUID.Peer)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
cancelFn()
|
cancelFn()
|
||||||
|
|
|
@ -814,6 +814,18 @@ func (s *ConfigSnapshot) MeshConfigTLSOutgoing() *structs.MeshDirectionalTLSConf
|
||||||
return mesh.TLS.Outgoing
|
return mesh.TLS.Outgoing
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *ConfigSnapshot) ToConfigSnapshotUpstreams() (*ConfigSnapshotUpstreams, error) {
|
||||||
|
switch s.Kind {
|
||||||
|
case structs.ServiceKindConnectProxy:
|
||||||
|
return &s.ConnectProxy.ConfigSnapshotUpstreams, nil
|
||||||
|
case structs.ServiceKindIngressGateway:
|
||||||
|
return &s.IngressGateway.ConfigSnapshotUpstreams, nil
|
||||||
|
default:
|
||||||
|
// This is a coherence check and should never fail
|
||||||
|
return nil, fmt.Errorf("No upstream snapshot for gateway mode %q", s.Kind)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (u *ConfigSnapshotUpstreams) UpstreamPeerMeta(uid UpstreamID) structs.PeeringServiceMeta {
|
func (u *ConfigSnapshotUpstreams) UpstreamPeerMeta(uid UpstreamID) structs.PeeringServiceMeta {
|
||||||
nodes, _ := u.PeerUpstreamEndpoints.Get(uid)
|
nodes, _ := u.PeerUpstreamEndpoints.Get(uid)
|
||||||
if len(nodes) == 0 {
|
if len(nodes) == 0 {
|
||||||
|
|
|
@ -493,6 +493,11 @@ func TestState_WatchesAndUpdates(t *testing.T) {
|
||||||
Mode: structs.MeshGatewayModeNone,
|
Mode: structs.MeshGatewayModeNone,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
structs.Upstream{
|
||||||
|
DestinationType: structs.UpstreamDestTypeService,
|
||||||
|
DestinationName: "api-failover-to-peer",
|
||||||
|
LocalBindPort: 10007,
|
||||||
|
},
|
||||||
structs.Upstream{
|
structs.Upstream{
|
||||||
DestinationType: structs.UpstreamDestTypeService,
|
DestinationType: structs.UpstreamDestTypeService,
|
||||||
DestinationName: "api-dc2",
|
DestinationName: "api-dc2",
|
||||||
|
@ -552,6 +557,16 @@ func TestState_WatchesAndUpdates(t *testing.T) {
|
||||||
Mode: structs.MeshGatewayModeNone,
|
Mode: structs.MeshGatewayModeNone,
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
|
fmt.Sprintf("discovery-chain:%s-failover-to-peer", apiUID.String()): genVerifyDiscoveryChainWatch(&structs.DiscoveryChainRequest{
|
||||||
|
Name: "api-failover-to-peer",
|
||||||
|
EvaluateInDatacenter: "dc1",
|
||||||
|
EvaluateInNamespace: "default",
|
||||||
|
EvaluateInPartition: "default",
|
||||||
|
Datacenter: "dc1",
|
||||||
|
OverrideMeshGateway: structs.MeshGatewayConfig{
|
||||||
|
Mode: meshGatewayProxyConfigValue,
|
||||||
|
},
|
||||||
|
}),
|
||||||
fmt.Sprintf("discovery-chain:%s-dc2", apiUID.String()): genVerifyDiscoveryChainWatch(&structs.DiscoveryChainRequest{
|
fmt.Sprintf("discovery-chain:%s-dc2", apiUID.String()): genVerifyDiscoveryChainWatch(&structs.DiscoveryChainRequest{
|
||||||
Name: "api-dc2",
|
Name: "api-dc2",
|
||||||
EvaluateInDatacenter: "dc1",
|
EvaluateInDatacenter: "dc1",
|
||||||
|
@ -639,6 +654,26 @@ func TestState_WatchesAndUpdates(t *testing.T) {
|
||||||
},
|
},
|
||||||
Err: nil,
|
Err: nil,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
CorrelationID: fmt.Sprintf("discovery-chain:%s-failover-to-peer", apiUID.String()),
|
||||||
|
Result: &structs.DiscoveryChainResponse{
|
||||||
|
Chain: discoverychain.TestCompileConfigEntries(t, "api-failover-to-peer", "default", "default", "dc1", "trustdomain.consul",
|
||||||
|
func(req *discoverychain.CompileRequest) {
|
||||||
|
req.OverrideMeshGateway.Mode = meshGatewayProxyConfigValue
|
||||||
|
}, &structs.ServiceResolverConfigEntry{
|
||||||
|
Kind: structs.ServiceResolver,
|
||||||
|
Name: "api-failover-to-peer",
|
||||||
|
Failover: map[string]structs.ServiceResolverFailover{
|
||||||
|
"*": {
|
||||||
|
Targets: []structs.ServiceResolverFailoverTarget{
|
||||||
|
{Peer: "cluster-01"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
Err: nil,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
verifySnapshot: func(t testing.TB, snap *ConfigSnapshot) {
|
verifySnapshot: func(t testing.TB, snap *ConfigSnapshot) {
|
||||||
require.True(t, snap.Valid())
|
require.True(t, snap.Valid())
|
||||||
|
@ -646,15 +681,18 @@ func TestState_WatchesAndUpdates(t *testing.T) {
|
||||||
require.Equal(t, indexedRoots, snap.Roots)
|
require.Equal(t, indexedRoots, snap.Roots)
|
||||||
|
|
||||||
require.Equal(t, issuedCert, snap.ConnectProxy.Leaf)
|
require.Equal(t, issuedCert, snap.ConnectProxy.Leaf)
|
||||||
require.Len(t, snap.ConnectProxy.DiscoveryChain, 5, "%+v", snap.ConnectProxy.DiscoveryChain)
|
require.Len(t, snap.ConnectProxy.DiscoveryChain, 6, "%+v", snap.ConnectProxy.DiscoveryChain)
|
||||||
require.Len(t, snap.ConnectProxy.WatchedUpstreams, 5, "%+v", snap.ConnectProxy.WatchedUpstreams)
|
require.Len(t, snap.ConnectProxy.WatchedUpstreams, 6, "%+v", snap.ConnectProxy.WatchedUpstreams)
|
||||||
require.Len(t, snap.ConnectProxy.WatchedUpstreamEndpoints, 5, "%+v", snap.ConnectProxy.WatchedUpstreamEndpoints)
|
require.Len(t, snap.ConnectProxy.WatchedUpstreamEndpoints, 6, "%+v", snap.ConnectProxy.WatchedUpstreamEndpoints)
|
||||||
require.Len(t, snap.ConnectProxy.WatchedGateways, 5, "%+v", snap.ConnectProxy.WatchedGateways)
|
require.Len(t, snap.ConnectProxy.WatchedGateways, 6, "%+v", snap.ConnectProxy.WatchedGateways)
|
||||||
require.Len(t, snap.ConnectProxy.WatchedGatewayEndpoints, 5, "%+v", snap.ConnectProxy.WatchedGatewayEndpoints)
|
require.Len(t, snap.ConnectProxy.WatchedGatewayEndpoints, 6, "%+v", snap.ConnectProxy.WatchedGatewayEndpoints)
|
||||||
|
|
||||||
require.Len(t, snap.ConnectProxy.WatchedServiceChecks, 0, "%+v", snap.ConnectProxy.WatchedServiceChecks)
|
require.Len(t, snap.ConnectProxy.WatchedServiceChecks, 0, "%+v", snap.ConnectProxy.WatchedServiceChecks)
|
||||||
require.Len(t, snap.ConnectProxy.PreparedQueryEndpoints, 0, "%+v", snap.ConnectProxy.PreparedQueryEndpoints)
|
require.Len(t, snap.ConnectProxy.PreparedQueryEndpoints, 0, "%+v", snap.ConnectProxy.PreparedQueryEndpoints)
|
||||||
|
|
||||||
|
require.Equal(t, 1, snap.ConnectProxy.ConfigSnapshotUpstreams.PeerUpstreamEndpoints.Len())
|
||||||
|
require.Equal(t, 1, snap.ConnectProxy.ConfigSnapshotUpstreams.UpstreamPeerTrustBundles.Len())
|
||||||
|
|
||||||
require.True(t, snap.ConnectProxy.IntentionsSet)
|
require.True(t, snap.ConnectProxy.IntentionsSet)
|
||||||
require.Equal(t, ixnMatch, snap.ConnectProxy.Intentions)
|
require.Equal(t, ixnMatch, snap.ConnectProxy.Intentions)
|
||||||
require.True(t, snap.ConnectProxy.MeshConfigSet)
|
require.True(t, snap.ConnectProxy.MeshConfigSet)
|
||||||
|
@ -667,6 +705,7 @@ func TestState_WatchesAndUpdates(t *testing.T) {
|
||||||
fmt.Sprintf("upstream-target:api-failover-remote.default.default.dc2:%s-failover-remote?dc=dc2", apiUID.String()): genVerifyServiceSpecificRequest("api-failover-remote", "", "dc2", true),
|
fmt.Sprintf("upstream-target:api-failover-remote.default.default.dc2:%s-failover-remote?dc=dc2", apiUID.String()): genVerifyServiceSpecificRequest("api-failover-remote", "", "dc2", true),
|
||||||
fmt.Sprintf("upstream-target:api-failover-local.default.default.dc2:%s-failover-local?dc=dc2", apiUID.String()): genVerifyServiceSpecificRequest("api-failover-local", "", "dc2", true),
|
fmt.Sprintf("upstream-target:api-failover-local.default.default.dc2:%s-failover-local?dc=dc2", apiUID.String()): genVerifyServiceSpecificRequest("api-failover-local", "", "dc2", true),
|
||||||
fmt.Sprintf("upstream-target:api-failover-direct.default.default.dc2:%s-failover-direct?dc=dc2", apiUID.String()): genVerifyServiceSpecificRequest("api-failover-direct", "", "dc2", true),
|
fmt.Sprintf("upstream-target:api-failover-direct.default.default.dc2:%s-failover-direct?dc=dc2", apiUID.String()): genVerifyServiceSpecificRequest("api-failover-direct", "", "dc2", true),
|
||||||
|
upstreamPeerWatchIDPrefix + fmt.Sprintf("%s-failover-to-peer?peer=cluster-01", apiUID.String()): genVerifyServiceSpecificPeeredRequest("api-failover-to-peer", "", "", "cluster-01", true),
|
||||||
fmt.Sprintf("mesh-gateway:dc2:%s-failover-remote?dc=dc2", apiUID.String()): genVerifyGatewayWatch("dc2"),
|
fmt.Sprintf("mesh-gateway:dc2:%s-failover-remote?dc=dc2", apiUID.String()): genVerifyGatewayWatch("dc2"),
|
||||||
fmt.Sprintf("mesh-gateway:dc1:%s-failover-local?dc=dc2", apiUID.String()): genVerifyGatewayWatch("dc1"),
|
fmt.Sprintf("mesh-gateway:dc1:%s-failover-local?dc=dc2", apiUID.String()): genVerifyGatewayWatch("dc1"),
|
||||||
},
|
},
|
||||||
|
@ -676,15 +715,18 @@ func TestState_WatchesAndUpdates(t *testing.T) {
|
||||||
require.Equal(t, indexedRoots, snap.Roots)
|
require.Equal(t, indexedRoots, snap.Roots)
|
||||||
|
|
||||||
require.Equal(t, issuedCert, snap.ConnectProxy.Leaf)
|
require.Equal(t, issuedCert, snap.ConnectProxy.Leaf)
|
||||||
require.Len(t, snap.ConnectProxy.DiscoveryChain, 5, "%+v", snap.ConnectProxy.DiscoveryChain)
|
require.Len(t, snap.ConnectProxy.DiscoveryChain, 6, "%+v", snap.ConnectProxy.DiscoveryChain)
|
||||||
require.Len(t, snap.ConnectProxy.WatchedUpstreams, 5, "%+v", snap.ConnectProxy.WatchedUpstreams)
|
require.Len(t, snap.ConnectProxy.WatchedUpstreams, 6, "%+v", snap.ConnectProxy.WatchedUpstreams)
|
||||||
require.Len(t, snap.ConnectProxy.WatchedUpstreamEndpoints, 5, "%+v", snap.ConnectProxy.WatchedUpstreamEndpoints)
|
require.Len(t, snap.ConnectProxy.WatchedUpstreamEndpoints, 6, "%+v", snap.ConnectProxy.WatchedUpstreamEndpoints)
|
||||||
require.Len(t, snap.ConnectProxy.WatchedGateways, 5, "%+v", snap.ConnectProxy.WatchedGateways)
|
require.Len(t, snap.ConnectProxy.WatchedGateways, 6, "%+v", snap.ConnectProxy.WatchedGateways)
|
||||||
require.Len(t, snap.ConnectProxy.WatchedGatewayEndpoints, 5, "%+v", snap.ConnectProxy.WatchedGatewayEndpoints)
|
require.Len(t, snap.ConnectProxy.WatchedGatewayEndpoints, 6, "%+v", snap.ConnectProxy.WatchedGatewayEndpoints)
|
||||||
|
|
||||||
require.Len(t, snap.ConnectProxy.WatchedServiceChecks, 0, "%+v", snap.ConnectProxy.WatchedServiceChecks)
|
require.Len(t, snap.ConnectProxy.WatchedServiceChecks, 0, "%+v", snap.ConnectProxy.WatchedServiceChecks)
|
||||||
require.Len(t, snap.ConnectProxy.PreparedQueryEndpoints, 0, "%+v", snap.ConnectProxy.PreparedQueryEndpoints)
|
require.Len(t, snap.ConnectProxy.PreparedQueryEndpoints, 0, "%+v", snap.ConnectProxy.PreparedQueryEndpoints)
|
||||||
|
|
||||||
|
require.Equal(t, 1, snap.ConnectProxy.ConfigSnapshotUpstreams.PeerUpstreamEndpoints.Len())
|
||||||
|
require.Equal(t, 1, snap.ConnectProxy.ConfigSnapshotUpstreams.UpstreamPeerTrustBundles.Len())
|
||||||
|
|
||||||
require.True(t, snap.ConnectProxy.IntentionsSet)
|
require.True(t, snap.ConnectProxy.IntentionsSet)
|
||||||
require.Equal(t, ixnMatch, snap.ConnectProxy.Intentions)
|
require.Equal(t, ixnMatch, snap.ConnectProxy.Intentions)
|
||||||
},
|
},
|
||||||
|
|
|
@ -280,6 +280,31 @@ func TestUpstreamNodesDC2(t testing.T) structs.CheckServiceNodes {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestUpstreamNodesPeerCluster01(t testing.T) structs.CheckServiceNodes {
|
||||||
|
peer := "cluster-01"
|
||||||
|
service := structs.TestNodeServiceWithNameInPeer(t, "web", peer)
|
||||||
|
return structs.CheckServiceNodes{
|
||||||
|
structs.CheckServiceNode{
|
||||||
|
Node: &structs.Node{
|
||||||
|
ID: "test1",
|
||||||
|
Node: "test1",
|
||||||
|
Address: "10.40.1.1",
|
||||||
|
PeerName: peer,
|
||||||
|
},
|
||||||
|
Service: service,
|
||||||
|
},
|
||||||
|
structs.CheckServiceNode{
|
||||||
|
Node: &structs.Node{
|
||||||
|
ID: "test2",
|
||||||
|
Node: "test2",
|
||||||
|
Address: "10.40.1.2",
|
||||||
|
PeerName: peer,
|
||||||
|
},
|
||||||
|
Service: service,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestUpstreamNodesInStatusDC2(t testing.T, status string) structs.CheckServiceNodes {
|
func TestUpstreamNodesInStatusDC2(t testing.T, status string) structs.CheckServiceNodes {
|
||||||
return structs.CheckServiceNodes{
|
return structs.CheckServiceNodes{
|
||||||
structs.CheckServiceNode{
|
structs.CheckServiceNode{
|
||||||
|
|
|
@ -8,6 +8,7 @@ import (
|
||||||
"github.com/hashicorp/consul/agent/connect"
|
"github.com/hashicorp/consul/agent/connect"
|
||||||
"github.com/hashicorp/consul/agent/consul/discoverychain"
|
"github.com/hashicorp/consul/agent/consul/discoverychain"
|
||||||
"github.com/hashicorp/consul/agent/structs"
|
"github.com/hashicorp/consul/agent/structs"
|
||||||
|
"github.com/hashicorp/consul/proto/pbpeering"
|
||||||
)
|
)
|
||||||
|
|
||||||
func setupTestVariationConfigEntriesAndSnapshot(
|
func setupTestVariationConfigEntriesAndSnapshot(
|
||||||
|
@ -72,6 +73,24 @@ func setupTestVariationConfigEntriesAndSnapshot(
|
||||||
Nodes: TestGatewayNodesDC2(t),
|
Nodes: TestGatewayNodesDC2(t),
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
case "failover-to-cluster-peer":
|
||||||
|
events = append(events, UpdateEvent{
|
||||||
|
CorrelationID: "peer-trust-bundle:cluster-01",
|
||||||
|
Result: &pbpeering.TrustBundleReadResponse{
|
||||||
|
Bundle: &pbpeering.PeeringTrustBundle{
|
||||||
|
PeerName: "peer1",
|
||||||
|
TrustDomain: "peer1.domain",
|
||||||
|
ExportedPartition: "peer1ap",
|
||||||
|
RootPEMs: []string{"peer1-root-1"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
events = append(events, UpdateEvent{
|
||||||
|
CorrelationID: "upstream-peer:db?peer=cluster-01",
|
||||||
|
Result: &structs.IndexedCheckServiceNodes{
|
||||||
|
Nodes: TestUpstreamNodesPeerCluster01(t),
|
||||||
|
},
|
||||||
|
})
|
||||||
case "failover-through-double-remote-gateway-triggered":
|
case "failover-through-double-remote-gateway-triggered":
|
||||||
events = append(events, UpdateEvent{
|
events = append(events, UpdateEvent{
|
||||||
CorrelationID: "upstream-target:db.default.default.dc1:" + dbUID.String(),
|
CorrelationID: "upstream-target:db.default.default.dc1:" + dbUID.String(),
|
||||||
|
@ -255,6 +274,21 @@ func setupTestVariationDiscoveryChain(
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
case "failover-to-cluster-peer":
|
||||||
|
entries = append(entries,
|
||||||
|
&structs.ServiceResolverConfigEntry{
|
||||||
|
Kind: structs.ServiceResolver,
|
||||||
|
Name: "db",
|
||||||
|
ConnectTimeout: 33 * time.Second,
|
||||||
|
Failover: map[string]structs.ServiceResolverFailover{
|
||||||
|
"*": {
|
||||||
|
Targets: []structs.ServiceResolverFailoverTarget{
|
||||||
|
{Peer: "cluster-01"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)
|
||||||
case "failover-through-double-remote-gateway-triggered":
|
case "failover-through-double-remote-gateway-triggered":
|
||||||
fallthrough
|
fallthrough
|
||||||
case "failover-through-double-remote-gateway":
|
case "failover-through-double-remote-gateway":
|
||||||
|
|
|
@ -9,7 +9,9 @@ import (
|
||||||
"github.com/mitchellh/mapstructure"
|
"github.com/mitchellh/mapstructure"
|
||||||
|
|
||||||
"github.com/hashicorp/consul/acl"
|
"github.com/hashicorp/consul/acl"
|
||||||
|
cachetype "github.com/hashicorp/consul/agent/cache-types"
|
||||||
"github.com/hashicorp/consul/agent/structs"
|
"github.com/hashicorp/consul/agent/structs"
|
||||||
|
"github.com/hashicorp/consul/proto/pbpeering"
|
||||||
)
|
)
|
||||||
|
|
||||||
type handlerUpstreams struct {
|
type handlerUpstreams struct {
|
||||||
|
@ -21,9 +23,10 @@ func (s *handlerUpstreams) handleUpdateUpstreams(ctx context.Context, u UpdateEv
|
||||||
return fmt.Errorf("error filling agent cache: %v", u.Err)
|
return fmt.Errorf("error filling agent cache: %v", u.Err)
|
||||||
}
|
}
|
||||||
|
|
||||||
upstreamsSnapshot := &snap.ConnectProxy.ConfigSnapshotUpstreams
|
upstreamsSnapshot, err := snap.ToConfigSnapshotUpstreams()
|
||||||
if snap.Kind == structs.ServiceKindIngressGateway {
|
|
||||||
upstreamsSnapshot = &snap.IngressGateway.ConfigSnapshotUpstreams
|
if err != nil {
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
switch {
|
switch {
|
||||||
|
@ -98,19 +101,16 @@ func (s *handlerUpstreams) handleUpdateUpstreams(ctx context.Context, u UpdateEv
|
||||||
|
|
||||||
uid := UpstreamIDFromString(uidString)
|
uid := UpstreamIDFromString(uidString)
|
||||||
|
|
||||||
filteredNodes := hostnameEndpoints(
|
s.setPeerEndpoints(upstreamsSnapshot, uid, resp.Nodes)
|
||||||
s.logger,
|
|
||||||
GatewayKey{ /*empty so it never matches*/ },
|
case strings.HasPrefix(u.CorrelationID, peerTrustBundleIDPrefix):
|
||||||
resp.Nodes,
|
resp, ok := u.Result.(*pbpeering.TrustBundleReadResponse)
|
||||||
)
|
if !ok {
|
||||||
if len(filteredNodes) > 0 {
|
return fmt.Errorf("invalid type for response: %T", u.Result)
|
||||||
if set := upstreamsSnapshot.PeerUpstreamEndpoints.Set(uid, filteredNodes); set {
|
}
|
||||||
upstreamsSnapshot.PeerUpstreamEndpointsUseHostnames[uid] = struct{}{}
|
peer := strings.TrimPrefix(u.CorrelationID, peerTrustBundleIDPrefix)
|
||||||
}
|
if resp.Bundle != nil {
|
||||||
} else {
|
upstreamsSnapshot.UpstreamPeerTrustBundles.Set(peer, resp.Bundle)
|
||||||
if set := upstreamsSnapshot.PeerUpstreamEndpoints.Set(uid, resp.Nodes); set {
|
|
||||||
delete(upstreamsSnapshot.PeerUpstreamEndpointsUseHostnames, uid)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
case strings.HasPrefix(u.CorrelationID, "upstream-target:"):
|
case strings.HasPrefix(u.CorrelationID, "upstream-target:"):
|
||||||
|
@ -216,6 +216,23 @@ func removeColonPrefix(s string) (string, string, bool) {
|
||||||
return s[0:idx], s[idx+1:], true
|
return s[0:idx], s[idx+1:], true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *handlerUpstreams) setPeerEndpoints(upstreamsSnapshot *ConfigSnapshotUpstreams, uid UpstreamID, nodes structs.CheckServiceNodes) {
|
||||||
|
filteredNodes := hostnameEndpoints(
|
||||||
|
s.logger,
|
||||||
|
GatewayKey{ /*empty so it never matches*/ },
|
||||||
|
nodes,
|
||||||
|
)
|
||||||
|
if len(filteredNodes) > 0 {
|
||||||
|
if set := upstreamsSnapshot.PeerUpstreamEndpoints.Set(uid, filteredNodes); set {
|
||||||
|
upstreamsSnapshot.PeerUpstreamEndpointsUseHostnames[uid] = struct{}{}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if set := upstreamsSnapshot.PeerUpstreamEndpoints.Set(uid, nodes); set {
|
||||||
|
delete(upstreamsSnapshot.PeerUpstreamEndpointsUseHostnames, uid)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (s *handlerUpstreams) resetWatchesFromChain(
|
func (s *handlerUpstreams) resetWatchesFromChain(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
uid UpstreamID,
|
uid UpstreamID,
|
||||||
|
@ -255,6 +272,12 @@ func (s *handlerUpstreams) resetWatchesFromChain(
|
||||||
delete(snap.WatchedUpstreams[uid], targetID)
|
delete(snap.WatchedUpstreams[uid], targetID)
|
||||||
delete(snap.WatchedUpstreamEndpoints[uid], targetID)
|
delete(snap.WatchedUpstreamEndpoints[uid], targetID)
|
||||||
cancelFn()
|
cancelFn()
|
||||||
|
|
||||||
|
targetUID := NewUpstreamIDFromTargetID(targetID)
|
||||||
|
if targetUID.Peer != "" {
|
||||||
|
snap.PeerUpstreamEndpoints.CancelWatch(targetUID)
|
||||||
|
snap.UpstreamPeerTrustBundles.CancelWatch(targetUID.Peer)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@ -274,6 +297,7 @@ func (s *handlerUpstreams) resetWatchesFromChain(
|
||||||
service: target.Service,
|
service: target.Service,
|
||||||
filter: target.Subset.Filter,
|
filter: target.Subset.Filter,
|
||||||
datacenter: target.Datacenter,
|
datacenter: target.Datacenter,
|
||||||
|
peer: target.Peer,
|
||||||
entMeta: target.GetEnterpriseMetadata(),
|
entMeta: target.GetEnterpriseMetadata(),
|
||||||
}
|
}
|
||||||
err := s.watchUpstreamTarget(ctx, snap, opts)
|
err := s.watchUpstreamTarget(ctx, snap, opts)
|
||||||
|
@ -384,6 +408,7 @@ type targetWatchOpts struct {
|
||||||
service string
|
service string
|
||||||
filter string
|
filter string
|
||||||
datacenter string
|
datacenter string
|
||||||
|
peer string
|
||||||
entMeta *acl.EnterpriseMeta
|
entMeta *acl.EnterpriseMeta
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -397,11 +422,17 @@ func (s *handlerUpstreams) watchUpstreamTarget(ctx context.Context, snap *Config
|
||||||
var finalMeta acl.EnterpriseMeta
|
var finalMeta acl.EnterpriseMeta
|
||||||
finalMeta.Merge(opts.entMeta)
|
finalMeta.Merge(opts.entMeta)
|
||||||
|
|
||||||
correlationID := "upstream-target:" + opts.chainID + ":" + opts.upstreamID.String()
|
uid := opts.upstreamID
|
||||||
|
correlationID := "upstream-target:" + opts.chainID + ":" + uid.String()
|
||||||
|
|
||||||
|
if opts.peer != "" {
|
||||||
|
uid = NewUpstreamIDFromTargetID(opts.chainID)
|
||||||
|
correlationID = upstreamPeerWatchIDPrefix + uid.String()
|
||||||
|
}
|
||||||
|
|
||||||
ctx, cancel := context.WithCancel(ctx)
|
ctx, cancel := context.WithCancel(ctx)
|
||||||
err := s.dataSources.Health.Notify(ctx, &structs.ServiceSpecificRequest{
|
err := s.dataSources.Health.Notify(ctx, &structs.ServiceSpecificRequest{
|
||||||
PeerName: opts.upstreamID.Peer,
|
PeerName: opts.peer,
|
||||||
Datacenter: opts.datacenter,
|
Datacenter: opts.datacenter,
|
||||||
QueryOptions: structs.QueryOptions{
|
QueryOptions: structs.QueryOptions{
|
||||||
Token: s.token,
|
Token: s.token,
|
||||||
|
@ -422,6 +453,31 @@ func (s *handlerUpstreams) watchUpstreamTarget(ctx context.Context, snap *Config
|
||||||
}
|
}
|
||||||
snap.WatchedUpstreams[opts.upstreamID][opts.chainID] = cancel
|
snap.WatchedUpstreams[opts.upstreamID][opts.chainID] = cancel
|
||||||
|
|
||||||
|
if uid.Peer == "" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if ok := snap.PeerUpstreamEndpoints.IsWatched(uid); !ok {
|
||||||
|
snap.PeerUpstreamEndpoints.InitWatch(uid, cancel)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check whether a watch for this peer exists to avoid duplicates.
|
||||||
|
if ok := snap.UpstreamPeerTrustBundles.IsWatched(uid.Peer); !ok {
|
||||||
|
peerCtx, cancel := context.WithCancel(ctx)
|
||||||
|
if err := s.dataSources.TrustBundle.Notify(peerCtx, &cachetype.TrustBundleReadRequest{
|
||||||
|
Request: &pbpeering.TrustBundleReadRequest{
|
||||||
|
Name: uid.Peer,
|
||||||
|
Partition: uid.PartitionOrDefault(),
|
||||||
|
},
|
||||||
|
QueryOptions: structs.QueryOptions{Token: s.token},
|
||||||
|
}, peerTrustBundleIDPrefix+uid.Peer, s.ch); err != nil {
|
||||||
|
cancel()
|
||||||
|
return fmt.Errorf("error while watching trust bundle for peer %q: %w", uid.Peer, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
snap.UpstreamPeerTrustBundles.InitWatch(uid.Peer, cancel)
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -726,11 +726,12 @@ func (s *Server) PeeringDelete(ctx context.Context, req *pbpeering.PeeringDelete
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if !existing.IsActive() {
|
if existing == nil || existing.State == pbpeering.PeeringState_DELETING {
|
||||||
// Return early when the Peering doesn't exist or is already marked for deletion.
|
// Return early when the Peering doesn't exist or is already marked for deletion.
|
||||||
// We don't return nil because the pb will fail to marshal.
|
// We don't return nil because the pb will fail to marshal.
|
||||||
return &pbpeering.PeeringDeleteResponse{}, nil
|
return &pbpeering.PeeringDeleteResponse{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// We are using a write request due to needing to perform a deferred deletion.
|
// We are using a write request due to needing to perform a deferred deletion.
|
||||||
// The peering gets marked for deletion by setting the DeletedAt field,
|
// The peering gets marked for deletion by setting the DeletedAt field,
|
||||||
// and a leader routine will handle deleting the peering.
|
// and a leader routine will handle deleting the peering.
|
||||||
|
|
|
@ -621,38 +621,50 @@ func TestPeeringService_Read_ACLEnforcement(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestPeeringService_Delete(t *testing.T) {
|
func TestPeeringService_Delete(t *testing.T) {
|
||||||
// TODO(peering): see note on newTestServer, refactor to not use this
|
tt := map[string]pbpeering.PeeringState{
|
||||||
s := newTestServer(t, nil)
|
"active peering": pbpeering.PeeringState_ACTIVE,
|
||||||
|
"terminated peering": pbpeering.PeeringState_TERMINATED,
|
||||||
p := &pbpeering.Peering{
|
|
||||||
ID: testUUID(t),
|
|
||||||
Name: "foo",
|
|
||||||
State: pbpeering.PeeringState_ESTABLISHING,
|
|
||||||
PeerCAPems: nil,
|
|
||||||
PeerServerName: "test",
|
|
||||||
PeerServerAddresses: []string{"addr1"},
|
|
||||||
}
|
}
|
||||||
err := s.Server.FSM().State().PeeringWrite(10, &pbpeering.PeeringWriteRequest{Peering: p})
|
|
||||||
require.NoError(t, err)
|
|
||||||
require.Nil(t, p.DeletedAt)
|
|
||||||
require.True(t, p.IsActive())
|
|
||||||
|
|
||||||
client := pbpeering.NewPeeringServiceClient(s.ClientConn(t))
|
for name, overrideState := range tt {
|
||||||
|
t.Run(name, func(t *testing.T) {
|
||||||
|
// TODO(peering): see note on newTestServer, refactor to not use this
|
||||||
|
s := newTestServer(t, nil)
|
||||||
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
// A pointer is kept for the following peering so that we can modify the object without another PeeringWrite.
|
||||||
t.Cleanup(cancel)
|
p := &pbpeering.Peering{
|
||||||
|
ID: testUUID(t),
|
||||||
|
Name: "foo",
|
||||||
|
PeerCAPems: nil,
|
||||||
|
PeerServerName: "test",
|
||||||
|
PeerServerAddresses: []string{"addr1"},
|
||||||
|
}
|
||||||
|
err := s.Server.FSM().State().PeeringWrite(10, &pbpeering.PeeringWriteRequest{Peering: p})
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Nil(t, p.DeletedAt)
|
||||||
|
require.True(t, p.IsActive())
|
||||||
|
|
||||||
_, err = client.PeeringDelete(ctx, &pbpeering.PeeringDeleteRequest{Name: "foo"})
|
// Overwrite the peering state to simulate deleting from a non-initial state.
|
||||||
require.NoError(t, err)
|
p.State = overrideState
|
||||||
|
|
||||||
retry.Run(t, func(r *retry.R) {
|
client := pbpeering.NewPeeringServiceClient(s.ClientConn(t))
|
||||||
_, resp, err := s.Server.FSM().State().PeeringRead(nil, state.Query{Value: "foo"})
|
|
||||||
require.NoError(r, err)
|
|
||||||
|
|
||||||
// Initially the peering will be marked for deletion but eventually the leader
|
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||||
// routine will clean it up.
|
t.Cleanup(cancel)
|
||||||
require.Nil(r, resp)
|
|
||||||
})
|
_, err = client.PeeringDelete(ctx, &pbpeering.PeeringDeleteRequest{Name: "foo"})
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
retry.Run(t, func(r *retry.R) {
|
||||||
|
_, resp, err := s.Server.FSM().State().PeeringRead(nil, state.Query{Value: "foo"})
|
||||||
|
require.NoError(r, err)
|
||||||
|
|
||||||
|
// Initially the peering will be marked for deletion but eventually the leader
|
||||||
|
// routine will clean it up.
|
||||||
|
require.Nil(r, resp)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestPeeringService_Delete_ACLEnforcement(t *testing.T) {
|
func TestPeeringService_Delete_ACLEnforcement(t *testing.T) {
|
||||||
|
|
|
@ -964,11 +964,18 @@ func (e *ServiceResolverConfigEntry) Validate() error {
|
||||||
|
|
||||||
// TODO(rb): prevent subsets and default subsets from being defined?
|
// TODO(rb): prevent subsets and default subsets from being defined?
|
||||||
|
|
||||||
if r.Service == "" && r.ServiceSubset == "" && r.Namespace == "" && r.Partition == "" && r.Datacenter == "" {
|
if r.isEmpty() {
|
||||||
return fmt.Errorf("Redirect is empty")
|
return fmt.Errorf("Redirect is empty")
|
||||||
}
|
}
|
||||||
|
|
||||||
if r.Service == "" {
|
switch {
|
||||||
|
case r.Peer != "" && r.ServiceSubset != "":
|
||||||
|
return fmt.Errorf("Redirect.Peer cannot be set with Redirect.ServiceSubset")
|
||||||
|
case r.Peer != "" && r.Partition != "":
|
||||||
|
return fmt.Errorf("Redirect.Partition cannot be set with Redirect.Peer")
|
||||||
|
case r.Peer != "" && r.Datacenter != "":
|
||||||
|
return fmt.Errorf("Redirect.Peer cannot be set with Redirect.Datacenter")
|
||||||
|
case r.Service == "":
|
||||||
if r.ServiceSubset != "" {
|
if r.ServiceSubset != "" {
|
||||||
return fmt.Errorf("Redirect.ServiceSubset defined without Redirect.Service")
|
return fmt.Errorf("Redirect.ServiceSubset defined without Redirect.Service")
|
||||||
}
|
}
|
||||||
|
@ -978,9 +985,12 @@ func (e *ServiceResolverConfigEntry) Validate() error {
|
||||||
if r.Partition != "" {
|
if r.Partition != "" {
|
||||||
return fmt.Errorf("Redirect.Partition defined without Redirect.Service")
|
return fmt.Errorf("Redirect.Partition defined without Redirect.Service")
|
||||||
}
|
}
|
||||||
} else if r.Service == e.Name {
|
if r.Peer != "" {
|
||||||
if r.ServiceSubset != "" && !isSubset(r.ServiceSubset) {
|
return fmt.Errorf("Redirect.Peer defined without Redirect.Service")
|
||||||
return fmt.Errorf("Redirect.ServiceSubset %q is not a valid subset of %q", r.ServiceSubset, r.Service)
|
}
|
||||||
|
case r.ServiceSubset != "" && (r.Service == "" || r.Service == e.Name):
|
||||||
|
if !isSubset(r.ServiceSubset) {
|
||||||
|
return fmt.Errorf("Redirect.ServiceSubset %q is not a valid subset of %q", r.ServiceSubset, e.Name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1231,6 +1241,10 @@ type ServiceResolverRedirect struct {
|
||||||
// Datacenter is the datacenter to resolve the service from instead of the
|
// Datacenter is the datacenter to resolve the service from instead of the
|
||||||
// current one (optional).
|
// current one (optional).
|
||||||
Datacenter string `json:",omitempty"`
|
Datacenter string `json:",omitempty"`
|
||||||
|
|
||||||
|
// Peer is the name of the cluster peer to resolve the service from instead
|
||||||
|
// of the current one (optional).
|
||||||
|
Peer string `json:",omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *ServiceResolverRedirect) ToDiscoveryTargetOpts() DiscoveryTargetOpts {
|
func (r *ServiceResolverRedirect) ToDiscoveryTargetOpts() DiscoveryTargetOpts {
|
||||||
|
@ -1240,9 +1254,14 @@ func (r *ServiceResolverRedirect) ToDiscoveryTargetOpts() DiscoveryTargetOpts {
|
||||||
Namespace: r.Namespace,
|
Namespace: r.Namespace,
|
||||||
Partition: r.Partition,
|
Partition: r.Partition,
|
||||||
Datacenter: r.Datacenter,
|
Datacenter: r.Datacenter,
|
||||||
|
Peer: r.Peer,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *ServiceResolverRedirect) isEmpty() bool {
|
||||||
|
return r.Service == "" && r.ServiceSubset == "" && r.Namespace == "" && r.Partition == "" && r.Datacenter == "" && r.Peer == ""
|
||||||
|
}
|
||||||
|
|
||||||
// There are some restrictions on what is allowed in here:
|
// There are some restrictions on what is allowed in here:
|
||||||
//
|
//
|
||||||
// - Service, ServiceSubset, Namespace, Datacenters, and Targets cannot all be
|
// - Service, ServiceSubset, Namespace, Datacenters, and Targets cannot all be
|
||||||
|
|
|
@ -72,6 +72,28 @@ func TestServiceResolverConfigEntry_OSS(t *testing.T) {
|
||||||
},
|
},
|
||||||
validateErr: `Bad Failover["*"]: Setting Namespace requires Consul Enterprise`,
|
validateErr: `Bad Failover["*"]: Setting Namespace requires Consul Enterprise`,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "setting redirect Namespace on OSS",
|
||||||
|
entry: &ServiceResolverConfigEntry{
|
||||||
|
Kind: ServiceResolver,
|
||||||
|
Name: "test",
|
||||||
|
Redirect: &ServiceResolverRedirect{
|
||||||
|
Namespace: "ns1",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
validateErr: `Redirect: Setting Namespace requires Consul Enterprise`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "setting redirect Partition on OSS",
|
||||||
|
entry: &ServiceResolverConfigEntry{
|
||||||
|
Kind: ServiceResolver,
|
||||||
|
Name: "test",
|
||||||
|
Redirect: &ServiceResolverRedirect{
|
||||||
|
Partition: "ap1",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
validateErr: `Redirect: Setting Partition requires Consul Enterprise`,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
// Bulk add a bunch of similar validation cases.
|
// Bulk add a bunch of similar validation cases.
|
||||||
|
|
|
@ -655,6 +655,41 @@ func TestServiceResolverConfigEntry(t *testing.T) {
|
||||||
},
|
},
|
||||||
validateErr: `Redirect.ServiceSubset "gone" is not a valid subset of "test"`,
|
validateErr: `Redirect.ServiceSubset "gone" is not a valid subset of "test"`,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "redirect with peer and subset",
|
||||||
|
entry: &ServiceResolverConfigEntry{
|
||||||
|
Kind: ServiceResolver,
|
||||||
|
Name: "test",
|
||||||
|
Redirect: &ServiceResolverRedirect{
|
||||||
|
Peer: "cluster-01",
|
||||||
|
ServiceSubset: "gone",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
validateErr: `Redirect.Peer cannot be set with Redirect.ServiceSubset`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "redirect with peer and datacenter",
|
||||||
|
entry: &ServiceResolverConfigEntry{
|
||||||
|
Kind: ServiceResolver,
|
||||||
|
Name: "test",
|
||||||
|
Redirect: &ServiceResolverRedirect{
|
||||||
|
Peer: "cluster-01",
|
||||||
|
Datacenter: "dc2",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
validateErr: `Redirect.Peer cannot be set with Redirect.Datacenter`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "redirect with peer and datacenter",
|
||||||
|
entry: &ServiceResolverConfigEntry{
|
||||||
|
Kind: ServiceResolver,
|
||||||
|
Name: "test",
|
||||||
|
Redirect: &ServiceResolverRedirect{
|
||||||
|
Peer: "cluster-01",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
validateErr: `Redirect.Peer defined without Redirect.Service`,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "self redirect with valid subset",
|
name: "self redirect with valid subset",
|
||||||
entry: &ServiceResolverConfigEntry{
|
entry: &ServiceResolverConfigEntry{
|
||||||
|
@ -669,6 +704,17 @@ func TestServiceResolverConfigEntry(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "redirect to peer",
|
||||||
|
entry: &ServiceResolverConfigEntry{
|
||||||
|
Kind: ServiceResolver,
|
||||||
|
Name: "test",
|
||||||
|
Redirect: &ServiceResolverRedirect{
|
||||||
|
Service: "other",
|
||||||
|
Peer: "cluster-01",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "simple wildcard failover",
|
name: "simple wildcard failover",
|
||||||
entry: &ServiceResolverConfigEntry{
|
entry: &ServiceResolverConfigEntry{
|
||||||
|
|
|
@ -53,6 +53,28 @@ func TestNodeServiceWithName(t testing.T, name string) *NodeService {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const peerTrustDomain = "1c053652-8512-4373-90cf-5a7f6263a994.consul"
|
||||||
|
|
||||||
|
func TestNodeServiceWithNameInPeer(t testing.T, name string, peer string) *NodeService {
|
||||||
|
service := "payments"
|
||||||
|
return &NodeService{
|
||||||
|
Kind: ServiceKindTypical,
|
||||||
|
Service: name,
|
||||||
|
Port: 8080,
|
||||||
|
Connect: ServiceConnect{
|
||||||
|
PeerMeta: &PeeringServiceMeta{
|
||||||
|
SNI: []string{
|
||||||
|
service + ".default.default." + peer + ".external." + peerTrustDomain,
|
||||||
|
},
|
||||||
|
SpiffeID: []string{
|
||||||
|
"spiffe://" + peerTrustDomain + "/ns/default/dc/" + peer + "-dc/svc/" + service,
|
||||||
|
},
|
||||||
|
Protocol: "tcp",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// TestNodeServiceProxy returns a *NodeService representing a valid
|
// TestNodeServiceProxy returns a *NodeService representing a valid
|
||||||
// Connect proxy.
|
// Connect proxy.
|
||||||
func TestNodeServiceProxy(t testing.T) *NodeService {
|
func TestNodeServiceProxy(t testing.T) *NodeService {
|
||||||
|
|
|
@ -88,29 +88,26 @@ func (s *ResourceGenerator) clustersFromSnapshotConnectProxy(cfgSnap *proxycfg.C
|
||||||
clusters = append(clusters, passthroughs...)
|
clusters = append(clusters, passthroughs...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// NOTE: Any time we skip a chain below we MUST also skip that discovery chain in endpoints.go
|
getUpstream := func(uid proxycfg.UpstreamID) (*structs.Upstream, bool) {
|
||||||
// so that the sets of endpoints generated matches the sets of clusters.
|
|
||||||
for uid, chain := range cfgSnap.ConnectProxy.DiscoveryChain {
|
|
||||||
upstream := cfgSnap.ConnectProxy.UpstreamConfig[uid]
|
upstream := cfgSnap.ConnectProxy.UpstreamConfig[uid]
|
||||||
|
|
||||||
explicit := upstream.HasLocalPortOrSocket()
|
explicit := upstream.HasLocalPortOrSocket()
|
||||||
implicit := cfgSnap.ConnectProxy.IsImplicitUpstream(uid)
|
implicit := cfgSnap.ConnectProxy.IsImplicitUpstream(uid)
|
||||||
if !implicit && !explicit {
|
return upstream, !implicit && !explicit
|
||||||
// Discovery chain is not associated with a known explicit or implicit upstream so it is skipped.
|
}
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
chainEndpoints, ok := cfgSnap.ConnectProxy.WatchedUpstreamEndpoints[uid]
|
// NOTE: Any time we skip a chain below we MUST also skip that discovery chain in endpoints.go
|
||||||
if !ok {
|
// so that the sets of endpoints generated matches the sets of clusters.
|
||||||
// this should not happen
|
for uid, chain := range cfgSnap.ConnectProxy.DiscoveryChain {
|
||||||
return nil, fmt.Errorf("no endpoint map for upstream %q", uid)
|
upstream, skip := getUpstream(uid)
|
||||||
|
if skip {
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
upstreamClusters, err := s.makeUpstreamClustersForDiscoveryChain(
|
upstreamClusters, err := s.makeUpstreamClustersForDiscoveryChain(
|
||||||
uid,
|
uid,
|
||||||
upstream,
|
upstream,
|
||||||
chain,
|
chain,
|
||||||
chainEndpoints,
|
|
||||||
cfgSnap,
|
cfgSnap,
|
||||||
false,
|
false,
|
||||||
)
|
)
|
||||||
|
@ -127,18 +124,15 @@ func (s *ResourceGenerator) clustersFromSnapshotConnectProxy(cfgSnap *proxycfg.C
|
||||||
// upstream in endpoints.go so that the sets of endpoints generated matches
|
// upstream in endpoints.go so that the sets of endpoints generated matches
|
||||||
// the sets of clusters.
|
// the sets of clusters.
|
||||||
for _, uid := range cfgSnap.ConnectProxy.PeeredUpstreamIDs() {
|
for _, uid := range cfgSnap.ConnectProxy.PeeredUpstreamIDs() {
|
||||||
upstreamCfg := cfgSnap.ConnectProxy.UpstreamConfig[uid]
|
upstream, skip := getUpstream(uid)
|
||||||
|
if skip {
|
||||||
explicit := upstreamCfg.HasLocalPortOrSocket()
|
|
||||||
implicit := cfgSnap.ConnectProxy.IsImplicitUpstream(uid)
|
|
||||||
if !implicit && !explicit {
|
|
||||||
// Not associated with a known explicit or implicit upstream so it is skipped.
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
peerMeta := cfgSnap.ConnectProxy.UpstreamPeerMeta(uid)
|
peerMeta := cfgSnap.ConnectProxy.UpstreamPeerMeta(uid)
|
||||||
|
cfg := s.getAndModifyUpstreamConfigForPeeredListener(uid, upstream, peerMeta)
|
||||||
|
|
||||||
upstreamCluster, err := s.makeUpstreamClusterForPeerService(uid, upstreamCfg, peerMeta, cfgSnap)
|
upstreamCluster, err := s.makeUpstreamClusterForPeerService(uid, cfg, peerMeta, cfgSnap)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -652,17 +646,10 @@ func (s *ResourceGenerator) clustersFromSnapshotIngressGateway(cfgSnap *proxycfg
|
||||||
return nil, fmt.Errorf("no discovery chain for upstream %q", uid)
|
return nil, fmt.Errorf("no discovery chain for upstream %q", uid)
|
||||||
}
|
}
|
||||||
|
|
||||||
chainEndpoints, ok := cfgSnap.IngressGateway.WatchedUpstreamEndpoints[uid]
|
|
||||||
if !ok {
|
|
||||||
// this should not happen
|
|
||||||
return nil, fmt.Errorf("no endpoint map for upstream %q", uid)
|
|
||||||
}
|
|
||||||
|
|
||||||
upstreamClusters, err := s.makeUpstreamClustersForDiscoveryChain(
|
upstreamClusters, err := s.makeUpstreamClustersForDiscoveryChain(
|
||||||
uid,
|
uid,
|
||||||
&u,
|
&u,
|
||||||
chain,
|
chain,
|
||||||
chainEndpoints,
|
|
||||||
cfgSnap,
|
cfgSnap,
|
||||||
false,
|
false,
|
||||||
)
|
)
|
||||||
|
@ -745,7 +732,7 @@ func (s *ResourceGenerator) makeAppCluster(cfgSnap *proxycfg.ConfigSnapshot, nam
|
||||||
|
|
||||||
func (s *ResourceGenerator) makeUpstreamClusterForPeerService(
|
func (s *ResourceGenerator) makeUpstreamClusterForPeerService(
|
||||||
uid proxycfg.UpstreamID,
|
uid proxycfg.UpstreamID,
|
||||||
upstream *structs.Upstream,
|
upstreamConfig structs.UpstreamConfig,
|
||||||
peerMeta structs.PeeringServiceMeta,
|
peerMeta structs.PeeringServiceMeta,
|
||||||
cfgSnap *proxycfg.ConfigSnapshot,
|
cfgSnap *proxycfg.ConfigSnapshot,
|
||||||
) (*envoy_cluster_v3.Cluster, error) {
|
) (*envoy_cluster_v3.Cluster, error) {
|
||||||
|
@ -754,16 +741,21 @@ func (s *ResourceGenerator) makeUpstreamClusterForPeerService(
|
||||||
err error
|
err error
|
||||||
)
|
)
|
||||||
|
|
||||||
cfg := s.getAndModifyUpstreamConfigForPeeredListener(uid, upstream, peerMeta)
|
if upstreamConfig.EnvoyClusterJSON != "" {
|
||||||
if cfg.EnvoyClusterJSON != "" {
|
c, err = makeClusterFromUserConfig(upstreamConfig.EnvoyClusterJSON)
|
||||||
c, err = makeClusterFromUserConfig(cfg.EnvoyClusterJSON)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return c, err
|
return c, err
|
||||||
}
|
}
|
||||||
// In the happy path don't return yet as we need to inject TLS config still.
|
// In the happy path don't return yet as we need to inject TLS config still.
|
||||||
}
|
}
|
||||||
|
|
||||||
tbs, ok := cfgSnap.ConnectProxy.UpstreamPeerTrustBundles.Get(uid.Peer)
|
upstreamsSnapshot, err := cfgSnap.ToConfigSnapshotUpstreams()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return c, err
|
||||||
|
}
|
||||||
|
|
||||||
|
tbs, ok := upstreamsSnapshot.UpstreamPeerTrustBundles.Get(uid.Peer)
|
||||||
if !ok {
|
if !ok {
|
||||||
// this should never happen since we loop through upstreams with
|
// this should never happen since we loop through upstreams with
|
||||||
// set trust bundles
|
// set trust bundles
|
||||||
|
@ -772,22 +764,29 @@ func (s *ResourceGenerator) makeUpstreamClusterForPeerService(
|
||||||
|
|
||||||
clusterName := generatePeeredClusterName(uid, tbs)
|
clusterName := generatePeeredClusterName(uid, tbs)
|
||||||
|
|
||||||
|
outlierDetection := ToOutlierDetection(upstreamConfig.PassiveHealthCheck)
|
||||||
|
// We can't rely on health checks for services on cluster peers because they
|
||||||
|
// don't take into account service resolvers, splitters and routers. Setting
|
||||||
|
// MaxEjectionPercent too 100% gives outlier detection the power to eject the
|
||||||
|
// entire cluster.
|
||||||
|
outlierDetection.MaxEjectionPercent = &wrappers.UInt32Value{Value: 100}
|
||||||
|
|
||||||
s.Logger.Trace("generating cluster for", "cluster", clusterName)
|
s.Logger.Trace("generating cluster for", "cluster", clusterName)
|
||||||
if c == nil {
|
if c == nil {
|
||||||
c = &envoy_cluster_v3.Cluster{
|
c = &envoy_cluster_v3.Cluster{
|
||||||
Name: clusterName,
|
Name: clusterName,
|
||||||
ConnectTimeout: durationpb.New(time.Duration(cfg.ConnectTimeoutMs) * time.Millisecond),
|
ConnectTimeout: durationpb.New(time.Duration(upstreamConfig.ConnectTimeoutMs) * time.Millisecond),
|
||||||
CommonLbConfig: &envoy_cluster_v3.Cluster_CommonLbConfig{
|
CommonLbConfig: &envoy_cluster_v3.Cluster_CommonLbConfig{
|
||||||
HealthyPanicThreshold: &envoy_type_v3.Percent{
|
HealthyPanicThreshold: &envoy_type_v3.Percent{
|
||||||
Value: 0, // disable panic threshold
|
Value: 0, // disable panic threshold
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
CircuitBreakers: &envoy_cluster_v3.CircuitBreakers{
|
CircuitBreakers: &envoy_cluster_v3.CircuitBreakers{
|
||||||
Thresholds: makeThresholdsIfNeeded(cfg.Limits),
|
Thresholds: makeThresholdsIfNeeded(upstreamConfig.Limits),
|
||||||
},
|
},
|
||||||
OutlierDetection: ToOutlierDetection(cfg.PassiveHealthCheck),
|
OutlierDetection: outlierDetection,
|
||||||
}
|
}
|
||||||
if cfg.Protocol == "http2" || cfg.Protocol == "grpc" {
|
if upstreamConfig.Protocol == "http2" || upstreamConfig.Protocol == "grpc" {
|
||||||
if err := s.setHttp2ProtocolOptions(c); err != nil {
|
if err := s.setHttp2ProtocolOptions(c); err != nil {
|
||||||
return c, err
|
return c, err
|
||||||
}
|
}
|
||||||
|
@ -821,12 +820,11 @@ func (s *ResourceGenerator) makeUpstreamClusterForPeerService(
|
||||||
false, /*onlyPassing*/
|
false, /*onlyPassing*/
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
rootPEMs := cfgSnap.RootPEMs()
|
rootPEMs := cfgSnap.RootPEMs()
|
||||||
if uid.Peer != "" {
|
if uid.Peer != "" {
|
||||||
tbs, _ := cfgSnap.ConnectProxy.UpstreamPeerTrustBundles.Get(uid.Peer)
|
tbs, _ := upstreamsSnapshot.UpstreamPeerTrustBundles.Get(uid.Peer)
|
||||||
rootPEMs = tbs.ConcatenatedRootPEMs()
|
rootPEMs = tbs.ConcatenatedRootPEMs()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -961,7 +959,6 @@ func (s *ResourceGenerator) makeUpstreamClustersForDiscoveryChain(
|
||||||
uid proxycfg.UpstreamID,
|
uid proxycfg.UpstreamID,
|
||||||
upstream *structs.Upstream,
|
upstream *structs.Upstream,
|
||||||
chain *structs.CompiledDiscoveryChain,
|
chain *structs.CompiledDiscoveryChain,
|
||||||
chainEndpoints map[string]structs.CheckServiceNodes,
|
|
||||||
cfgSnap *proxycfg.ConfigSnapshot,
|
cfgSnap *proxycfg.ConfigSnapshot,
|
||||||
forMeshGateway bool,
|
forMeshGateway bool,
|
||||||
) ([]*envoy_cluster_v3.Cluster, error) {
|
) ([]*envoy_cluster_v3.Cluster, error) {
|
||||||
|
@ -978,7 +975,15 @@ func (s *ResourceGenerator) makeUpstreamClustersForDiscoveryChain(
|
||||||
upstreamConfigMap = upstream.Config
|
upstreamConfigMap = upstream.Config
|
||||||
}
|
}
|
||||||
|
|
||||||
cfg, err := structs.ParseUpstreamConfigNoDefaults(upstreamConfigMap)
|
upstreamsSnapshot, err := cfgSnap.ToConfigSnapshotUpstreams()
|
||||||
|
|
||||||
|
// Mesh gateways are exempt because upstreamsSnapshot is only used for
|
||||||
|
// cluster peering targets and transative failover/redirects are unsupported.
|
||||||
|
if err != nil && !forMeshGateway {
|
||||||
|
return nil, fmt.Errorf("No upstream snapshot for gateway mode %q", cfgSnap.Kind)
|
||||||
|
}
|
||||||
|
|
||||||
|
rawUpstreamConfig, err := structs.ParseUpstreamConfigNoDefaults(upstreamConfigMap)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// Don't hard fail on a config typo, just warn. The parse func returns
|
// Don't hard fail on a config typo, just warn. The parse func returns
|
||||||
// default config if there is an error so it's safe to continue.
|
// default config if there is an error so it's safe to continue.
|
||||||
|
@ -986,13 +991,28 @@ func (s *ResourceGenerator) makeUpstreamClustersForDiscoveryChain(
|
||||||
"error", err)
|
"error", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
finalizeUpstreamConfig := func(cfg structs.UpstreamConfig, connectTimeout time.Duration) structs.UpstreamConfig {
|
||||||
|
if cfg.Protocol == "" {
|
||||||
|
cfg.Protocol = chain.Protocol
|
||||||
|
}
|
||||||
|
|
||||||
|
if cfg.Protocol == "" {
|
||||||
|
cfg.Protocol = "tcp"
|
||||||
|
}
|
||||||
|
|
||||||
|
if cfg.ConnectTimeoutMs == 0 {
|
||||||
|
cfg.ConnectTimeoutMs = int(connectTimeout / time.Millisecond)
|
||||||
|
}
|
||||||
|
return cfg
|
||||||
|
}
|
||||||
|
|
||||||
var escapeHatchCluster *envoy_cluster_v3.Cluster
|
var escapeHatchCluster *envoy_cluster_v3.Cluster
|
||||||
if !forMeshGateway {
|
if !forMeshGateway {
|
||||||
if cfg.EnvoyClusterJSON != "" {
|
if rawUpstreamConfig.EnvoyClusterJSON != "" {
|
||||||
if chain.Default {
|
if chain.Default {
|
||||||
// If you haven't done anything to setup the discovery chain, then
|
// If you haven't done anything to setup the discovery chain, then
|
||||||
// you can use the envoy_cluster_json escape hatch.
|
// you can use the envoy_cluster_json escape hatch.
|
||||||
escapeHatchCluster, err = makeClusterFromUserConfig(cfg.EnvoyClusterJSON)
|
escapeHatchCluster, err = makeClusterFromUserConfig(rawUpstreamConfig.EnvoyClusterJSON)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -1006,14 +1026,20 @@ func (s *ResourceGenerator) makeUpstreamClustersForDiscoveryChain(
|
||||||
|
|
||||||
var out []*envoy_cluster_v3.Cluster
|
var out []*envoy_cluster_v3.Cluster
|
||||||
for _, node := range chain.Nodes {
|
for _, node := range chain.Nodes {
|
||||||
if node.Type != structs.DiscoveryGraphNodeTypeResolver {
|
switch {
|
||||||
|
case node == nil:
|
||||||
|
return nil, fmt.Errorf("impossible to process a nil node")
|
||||||
|
case node.Type != structs.DiscoveryGraphNodeTypeResolver:
|
||||||
continue
|
continue
|
||||||
|
case node.Resolver == nil:
|
||||||
|
return nil, fmt.Errorf("impossible to process a non-resolver node")
|
||||||
}
|
}
|
||||||
failover := node.Resolver.Failover
|
failover := node.Resolver.Failover
|
||||||
// These variables are prefixed with primary to avoid shaddowing bugs.
|
// These variables are prefixed with primary to avoid shaddowing bugs.
|
||||||
primaryTargetID := node.Resolver.Target
|
primaryTargetID := node.Resolver.Target
|
||||||
primaryTarget := chain.Targets[primaryTargetID]
|
primaryTarget := chain.Targets[primaryTargetID]
|
||||||
primaryClusterName := CustomizeClusterName(primaryTarget.Name, chain)
|
primaryClusterName := CustomizeClusterName(primaryTarget.Name, chain)
|
||||||
|
upstreamConfig := finalizeUpstreamConfig(rawUpstreamConfig, node.Resolver.ConnectTimeout)
|
||||||
if forMeshGateway {
|
if forMeshGateway {
|
||||||
primaryClusterName = meshGatewayExportedClusterNamePrefix + primaryClusterName
|
primaryClusterName = meshGatewayExportedClusterNamePrefix + primaryClusterName
|
||||||
}
|
}
|
||||||
|
@ -1026,22 +1052,38 @@ func (s *ResourceGenerator) makeUpstreamClustersForDiscoveryChain(
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
type targetClusterOptions struct {
|
type targetClusterOption struct {
|
||||||
targetID string
|
targetID string
|
||||||
clusterName string
|
clusterName string
|
||||||
}
|
}
|
||||||
|
|
||||||
// Construct the information required to make target clusters. When
|
// Construct the information required to make target clusters. When
|
||||||
// failover is configured, create the aggregate cluster.
|
// failover is configured, create the aggregate cluster.
|
||||||
var targetClustersOptions []targetClusterOptions
|
var targetClustersOptions []targetClusterOption
|
||||||
if failover != nil && !forMeshGateway {
|
if failover != nil && !forMeshGateway {
|
||||||
var failoverClusterNames []string
|
var failoverClusterNames []string
|
||||||
for _, tid := range append([]string{primaryTargetID}, failover.Targets...) {
|
for _, tid := range append([]string{primaryTargetID}, failover.Targets...) {
|
||||||
target := chain.Targets[tid]
|
target := chain.Targets[tid]
|
||||||
clusterName := CustomizeClusterName(target.Name, chain)
|
clusterName := target.Name
|
||||||
|
targetUID := proxycfg.NewUpstreamIDFromTargetID(tid)
|
||||||
|
if targetUID.Peer != "" {
|
||||||
|
tbs, ok := upstreamsSnapshot.UpstreamPeerTrustBundles.Get(targetUID.Peer)
|
||||||
|
// We can't generate cluster on peers without the trust bundle. The
|
||||||
|
// trust bundle should be ready soon.
|
||||||
|
if !ok {
|
||||||
|
s.Logger.Debug("peer trust bundle not ready for discovery chain target",
|
||||||
|
"peer", targetUID.Peer,
|
||||||
|
"target", tid,
|
||||||
|
)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
clusterName = generatePeeredClusterName(targetUID, tbs)
|
||||||
|
}
|
||||||
|
clusterName = CustomizeClusterName(clusterName, chain)
|
||||||
clusterName = failoverClusterNamePrefix + clusterName
|
clusterName = failoverClusterNamePrefix + clusterName
|
||||||
|
|
||||||
targetClustersOptions = append(targetClustersOptions, targetClusterOptions{
|
targetClustersOptions = append(targetClustersOptions, targetClusterOption{
|
||||||
targetID: tid,
|
targetID: tid,
|
||||||
clusterName: clusterName,
|
clusterName: clusterName,
|
||||||
})
|
})
|
||||||
|
@ -1070,7 +1112,7 @@ func (s *ResourceGenerator) makeUpstreamClustersForDiscoveryChain(
|
||||||
|
|
||||||
out = append(out, c)
|
out = append(out, c)
|
||||||
} else {
|
} else {
|
||||||
targetClustersOptions = append(targetClustersOptions, targetClusterOptions{
|
targetClustersOptions = append(targetClustersOptions, targetClusterOption{
|
||||||
targetID: primaryTargetID,
|
targetID: primaryTargetID,
|
||||||
clusterName: primaryClusterName,
|
clusterName: primaryClusterName,
|
||||||
})
|
})
|
||||||
|
@ -1089,11 +1131,20 @@ func (s *ResourceGenerator) makeUpstreamClustersForDiscoveryChain(
|
||||||
Datacenter: target.Datacenter,
|
Datacenter: target.Datacenter,
|
||||||
Service: target.Service,
|
Service: target.Service,
|
||||||
}.URI().String()
|
}.URI().String()
|
||||||
if uid.Peer != "" {
|
targetUID := proxycfg.NewUpstreamIDFromTargetID(targetInfo.targetID)
|
||||||
return nil, fmt.Errorf("impossible to get a peer discovery chain")
|
s.Logger.Debug("generating cluster for", "cluster", targetInfo.clusterName)
|
||||||
|
if targetUID.Peer != "" {
|
||||||
|
peerMeta := upstreamsSnapshot.UpstreamPeerMeta(targetUID)
|
||||||
|
upstreamCluster, err := s.makeUpstreamClusterForPeerService(targetUID, upstreamConfig, peerMeta, cfgSnap)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// Override the cluster name to include the failover-target~ prefix.
|
||||||
|
upstreamCluster.Name = targetInfo.clusterName
|
||||||
|
out = append(out, upstreamCluster)
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
s.Logger.Trace("generating cluster for", "cluster", targetInfo.clusterName)
|
|
||||||
c := &envoy_cluster_v3.Cluster{
|
c := &envoy_cluster_v3.Cluster{
|
||||||
Name: targetInfo.clusterName,
|
Name: targetInfo.clusterName,
|
||||||
AltStatName: targetInfo.clusterName,
|
AltStatName: targetInfo.clusterName,
|
||||||
|
@ -1114,9 +1165,9 @@ func (s *ResourceGenerator) makeUpstreamClustersForDiscoveryChain(
|
||||||
},
|
},
|
||||||
// TODO(peering): make circuit breakers or outlier detection work?
|
// TODO(peering): make circuit breakers or outlier detection work?
|
||||||
CircuitBreakers: &envoy_cluster_v3.CircuitBreakers{
|
CircuitBreakers: &envoy_cluster_v3.CircuitBreakers{
|
||||||
Thresholds: makeThresholdsIfNeeded(cfg.Limits),
|
Thresholds: makeThresholdsIfNeeded(upstreamConfig.Limits),
|
||||||
},
|
},
|
||||||
OutlierDetection: ToOutlierDetection(cfg.PassiveHealthCheck),
|
OutlierDetection: ToOutlierDetection(upstreamConfig.PassiveHealthCheck),
|
||||||
}
|
}
|
||||||
|
|
||||||
var lb *structs.LoadBalancer
|
var lb *structs.LoadBalancer
|
||||||
|
@ -1127,19 +1178,7 @@ func (s *ResourceGenerator) makeUpstreamClustersForDiscoveryChain(
|
||||||
return nil, fmt.Errorf("failed to apply load balancer configuration to cluster %q: %v", targetInfo.clusterName, err)
|
return nil, fmt.Errorf("failed to apply load balancer configuration to cluster %q: %v", targetInfo.clusterName, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
var proto string
|
if upstreamConfig.Protocol == "http2" || upstreamConfig.Protocol == "grpc" {
|
||||||
if !forMeshGateway {
|
|
||||||
proto = cfg.Protocol
|
|
||||||
}
|
|
||||||
if proto == "" {
|
|
||||||
proto = chain.Protocol
|
|
||||||
}
|
|
||||||
|
|
||||||
if proto == "" {
|
|
||||||
proto = "tcp"
|
|
||||||
}
|
|
||||||
|
|
||||||
if proto == "http2" || proto == "grpc" {
|
|
||||||
if err := s.setHttp2ProtocolOptions(c); err != nil {
|
if err := s.setHttp2ProtocolOptions(c); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -1148,7 +1187,7 @@ func (s *ResourceGenerator) makeUpstreamClustersForDiscoveryChain(
|
||||||
configureTLS := true
|
configureTLS := true
|
||||||
if forMeshGateway {
|
if forMeshGateway {
|
||||||
// We only initiate TLS if we're doing an L7 proxy.
|
// We only initiate TLS if we're doing an L7 proxy.
|
||||||
configureTLS = structs.IsProtocolHTTPLike(proto)
|
configureTLS = structs.IsProtocolHTTPLike(upstreamConfig.Protocol)
|
||||||
}
|
}
|
||||||
|
|
||||||
if configureTLS {
|
if configureTLS {
|
||||||
|
@ -1221,7 +1260,6 @@ func (s *ResourceGenerator) makeExportedUpstreamClustersForMeshGateway(cfgSnap *
|
||||||
proxycfg.NewUpstreamIDFromServiceName(svc),
|
proxycfg.NewUpstreamIDFromServiceName(svc),
|
||||||
nil,
|
nil,
|
||||||
chain,
|
chain,
|
||||||
nil,
|
|
||||||
cfgSnap,
|
cfgSnap,
|
||||||
true,
|
true,
|
||||||
)
|
)
|
||||||
|
|
|
@ -257,6 +257,12 @@ func TestClustersFromSnapshot(t *testing.T) {
|
||||||
return proxycfg.TestConfigSnapshotDiscoveryChain(t, "failover", nil, nil)
|
return proxycfg.TestConfigSnapshotDiscoveryChain(t, "failover", nil, nil)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "connect-proxy-with-chain-and-failover-to-cluster-peer",
|
||||||
|
create: func(t testinf.T) *proxycfg.ConfigSnapshot {
|
||||||
|
return proxycfg.TestConfigSnapshotDiscoveryChain(t, "failover-to-cluster-peer", nil, nil)
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "connect-proxy-with-tcp-chain-failover-through-remote-gateway",
|
name: "connect-proxy-with-tcp-chain-failover-through-remote-gateway",
|
||||||
create: func(t testinf.T) *proxycfg.ConfigSnapshot {
|
create: func(t testinf.T) *proxycfg.ConfigSnapshot {
|
||||||
|
@ -495,6 +501,13 @@ func TestClustersFromSnapshot(t *testing.T) {
|
||||||
"failover", nil, nil, nil)
|
"failover", nil, nil, nil)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "ingress-with-chain-and-failover-to-cluster-peer",
|
||||||
|
create: func(t testinf.T) *proxycfg.ConfigSnapshot {
|
||||||
|
return proxycfg.TestConfigSnapshotIngressGateway(t, true, "tcp",
|
||||||
|
"failover-to-cluster-peer", nil, nil, nil)
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "ingress-with-tcp-chain-failover-through-remote-gateway",
|
name: "ingress-with-tcp-chain-failover-through-remote-gateway",
|
||||||
create: func(t testinf.T) *proxycfg.ConfigSnapshot {
|
create: func(t testinf.T) *proxycfg.ConfigSnapshot {
|
||||||
|
|
|
@ -50,14 +50,19 @@ func (s *ResourceGenerator) endpointsFromSnapshotConnectProxy(cfgSnap *proxycfg.
|
||||||
cfgSnap.ConnectProxy.PeerUpstreamEndpoints.Len()+
|
cfgSnap.ConnectProxy.PeerUpstreamEndpoints.Len()+
|
||||||
len(cfgSnap.ConnectProxy.WatchedUpstreamEndpoints))
|
len(cfgSnap.ConnectProxy.WatchedUpstreamEndpoints))
|
||||||
|
|
||||||
// NOTE: Any time we skip a chain below we MUST also skip that discovery chain in clusters.go
|
getUpstream := func(uid proxycfg.UpstreamID) (*structs.Upstream, bool) {
|
||||||
// so that the sets of endpoints generated matches the sets of clusters.
|
|
||||||
for uid, chain := range cfgSnap.ConnectProxy.DiscoveryChain {
|
|
||||||
upstream := cfgSnap.ConnectProxy.UpstreamConfig[uid]
|
upstream := cfgSnap.ConnectProxy.UpstreamConfig[uid]
|
||||||
|
|
||||||
explicit := upstream.HasLocalPortOrSocket()
|
explicit := upstream.HasLocalPortOrSocket()
|
||||||
implicit := cfgSnap.ConnectProxy.IsImplicitUpstream(uid)
|
implicit := cfgSnap.ConnectProxy.IsImplicitUpstream(uid)
|
||||||
if !implicit && !explicit {
|
return upstream, !implicit && !explicit
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE: Any time we skip a chain below we MUST also skip that discovery chain in clusters.go
|
||||||
|
// so that the sets of endpoints generated matches the sets of clusters.
|
||||||
|
for uid, chain := range cfgSnap.ConnectProxy.DiscoveryChain {
|
||||||
|
upstream, skip := getUpstream(uid)
|
||||||
|
if skip {
|
||||||
// Discovery chain is not associated with a known explicit or implicit upstream so it is skipped.
|
// Discovery chain is not associated with a known explicit or implicit upstream so it is skipped.
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
@ -70,6 +75,7 @@ func (s *ResourceGenerator) endpointsFromSnapshotConnectProxy(cfgSnap *proxycfg.
|
||||||
es, err := s.endpointsFromDiscoveryChain(
|
es, err := s.endpointsFromDiscoveryChain(
|
||||||
uid,
|
uid,
|
||||||
chain,
|
chain,
|
||||||
|
cfgSnap,
|
||||||
cfgSnap.Locality,
|
cfgSnap.Locality,
|
||||||
upstreamConfigMap,
|
upstreamConfigMap,
|
||||||
cfgSnap.ConnectProxy.WatchedUpstreamEndpoints[uid],
|
cfgSnap.ConnectProxy.WatchedUpstreamEndpoints[uid],
|
||||||
|
@ -86,12 +92,9 @@ func (s *ResourceGenerator) endpointsFromSnapshotConnectProxy(cfgSnap *proxycfg.
|
||||||
// upstream in clusters.go so that the sets of endpoints generated matches
|
// upstream in clusters.go so that the sets of endpoints generated matches
|
||||||
// the sets of clusters.
|
// the sets of clusters.
|
||||||
for _, uid := range cfgSnap.ConnectProxy.PeeredUpstreamIDs() {
|
for _, uid := range cfgSnap.ConnectProxy.PeeredUpstreamIDs() {
|
||||||
upstreamCfg := cfgSnap.ConnectProxy.UpstreamConfig[uid]
|
_, skip := getUpstream(uid)
|
||||||
|
if skip {
|
||||||
explicit := upstreamCfg.HasLocalPortOrSocket()
|
// Discovery chain is not associated with a known explicit or implicit upstream so it is skipped.
|
||||||
implicit := cfgSnap.ConnectProxy.IsImplicitUpstream(uid)
|
|
||||||
if !implicit && !explicit {
|
|
||||||
// Not associated with a known explicit or implicit upstream so it is skipped.
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -104,22 +107,14 @@ func (s *ResourceGenerator) endpointsFromSnapshotConnectProxy(cfgSnap *proxycfg.
|
||||||
|
|
||||||
clusterName := generatePeeredClusterName(uid, tbs)
|
clusterName := generatePeeredClusterName(uid, tbs)
|
||||||
|
|
||||||
// Also skip peer instances with a hostname as their address. EDS
|
loadAssignment, err := s.makeUpstreamLoadAssignmentForPeerService(cfgSnap, clusterName, uid)
|
||||||
// cannot resolve hostnames, so we provide them through CDS instead.
|
|
||||||
if _, ok := cfgSnap.ConnectProxy.PeerUpstreamEndpointsUseHostnames[uid]; ok {
|
if err != nil {
|
||||||
continue
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
endpoints, ok := cfgSnap.ConnectProxy.PeerUpstreamEndpoints.Get(uid)
|
if loadAssignment != nil {
|
||||||
if ok {
|
resources = append(resources, loadAssignment)
|
||||||
la := makeLoadAssignment(
|
|
||||||
clusterName,
|
|
||||||
[]loadAssignmentEndpointGroup{
|
|
||||||
{Endpoints: endpoints},
|
|
||||||
},
|
|
||||||
proxycfg.GatewayKey{ /*empty so it never matches*/ },
|
|
||||||
)
|
|
||||||
resources = append(resources, la)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -375,6 +370,7 @@ func (s *ResourceGenerator) endpointsFromSnapshotIngressGateway(cfgSnap *proxycf
|
||||||
es, err := s.endpointsFromDiscoveryChain(
|
es, err := s.endpointsFromDiscoveryChain(
|
||||||
uid,
|
uid,
|
||||||
cfgSnap.IngressGateway.DiscoveryChain[uid],
|
cfgSnap.IngressGateway.DiscoveryChain[uid],
|
||||||
|
cfgSnap,
|
||||||
proxycfg.GatewayKey{Datacenter: cfgSnap.Datacenter, Partition: u.DestinationPartition},
|
proxycfg.GatewayKey{Datacenter: cfgSnap.Datacenter, Partition: u.DestinationPartition},
|
||||||
u.Config,
|
u.Config,
|
||||||
cfgSnap.IngressGateway.WatchedUpstreamEndpoints[uid],
|
cfgSnap.IngressGateway.WatchedUpstreamEndpoints[uid],
|
||||||
|
@ -412,9 +408,38 @@ func makePipeEndpoint(path string) *envoy_endpoint_v3.LbEndpoint {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *ResourceGenerator) makeUpstreamLoadAssignmentForPeerService(cfgSnap *proxycfg.ConfigSnapshot, clusterName string, uid proxycfg.UpstreamID) (*envoy_endpoint_v3.ClusterLoadAssignment, error) {
|
||||||
|
var la *envoy_endpoint_v3.ClusterLoadAssignment
|
||||||
|
|
||||||
|
upstreamsSnapshot, err := cfgSnap.ToConfigSnapshotUpstreams()
|
||||||
|
if err != nil {
|
||||||
|
return la, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Also skip peer instances with a hostname as their address. EDS
|
||||||
|
// cannot resolve hostnames, so we provide them through CDS instead.
|
||||||
|
if _, ok := upstreamsSnapshot.PeerUpstreamEndpointsUseHostnames[uid]; ok {
|
||||||
|
return la, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
endpoints, ok := upstreamsSnapshot.PeerUpstreamEndpoints.Get(uid)
|
||||||
|
if !ok {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
la = makeLoadAssignment(
|
||||||
|
clusterName,
|
||||||
|
[]loadAssignmentEndpointGroup{
|
||||||
|
{Endpoints: endpoints},
|
||||||
|
},
|
||||||
|
proxycfg.GatewayKey{ /*empty so it never matches*/ },
|
||||||
|
)
|
||||||
|
return la, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (s *ResourceGenerator) endpointsFromDiscoveryChain(
|
func (s *ResourceGenerator) endpointsFromDiscoveryChain(
|
||||||
uid proxycfg.UpstreamID,
|
uid proxycfg.UpstreamID,
|
||||||
chain *structs.CompiledDiscoveryChain,
|
chain *structs.CompiledDiscoveryChain,
|
||||||
|
cfgSnap *proxycfg.ConfigSnapshot,
|
||||||
gatewayKey proxycfg.GatewayKey,
|
gatewayKey proxycfg.GatewayKey,
|
||||||
upstreamConfigMap map[string]interface{},
|
upstreamConfigMap map[string]interface{},
|
||||||
upstreamEndpoints map[string]structs.CheckServiceNodes,
|
upstreamEndpoints map[string]structs.CheckServiceNodes,
|
||||||
|
@ -432,6 +457,14 @@ func (s *ResourceGenerator) endpointsFromDiscoveryChain(
|
||||||
upstreamConfigMap = make(map[string]interface{}) // TODO:needed?
|
upstreamConfigMap = make(map[string]interface{}) // TODO:needed?
|
||||||
}
|
}
|
||||||
|
|
||||||
|
upstreamsSnapshot, err := cfgSnap.ToConfigSnapshotUpstreams()
|
||||||
|
|
||||||
|
// Mesh gateways are exempt because upstreamsSnapshot is only used for
|
||||||
|
// cluster peering targets and transative failover/redirects are unsupported.
|
||||||
|
if err != nil && !forMeshGateway {
|
||||||
|
return nil, fmt.Errorf("No upstream snapshot for gateway mode %q", cfgSnap.Kind)
|
||||||
|
}
|
||||||
|
|
||||||
var resources []proto.Message
|
var resources []proto.Message
|
||||||
|
|
||||||
var escapeHatchCluster *envoy_cluster_v3.Cluster
|
var escapeHatchCluster *envoy_cluster_v3.Cluster
|
||||||
|
@ -465,8 +498,15 @@ func (s *ResourceGenerator) endpointsFromDiscoveryChain(
|
||||||
if node.Type != structs.DiscoveryGraphNodeTypeResolver {
|
if node.Type != structs.DiscoveryGraphNodeTypeResolver {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
primaryTargetID := node.Resolver.Target
|
||||||
failover := node.Resolver.Failover
|
failover := node.Resolver.Failover
|
||||||
|
|
||||||
|
type targetLoadAssignmentOption struct {
|
||||||
|
targetID string
|
||||||
|
clusterName string
|
||||||
|
}
|
||||||
|
var targetLoadAssignmentOptions []targetLoadAssignmentOption
|
||||||
|
|
||||||
var numFailoverTargets int
|
var numFailoverTargets int
|
||||||
if failover != nil {
|
if failover != nil {
|
||||||
numFailoverTargets = len(failover.Targets)
|
numFailoverTargets = len(failover.Targets)
|
||||||
|
@ -474,66 +514,84 @@ func (s *ResourceGenerator) endpointsFromDiscoveryChain(
|
||||||
clusterNamePrefix := ""
|
clusterNamePrefix := ""
|
||||||
if numFailoverTargets > 0 && !forMeshGateway {
|
if numFailoverTargets > 0 && !forMeshGateway {
|
||||||
clusterNamePrefix = failoverClusterNamePrefix
|
clusterNamePrefix = failoverClusterNamePrefix
|
||||||
for _, failTargetID := range failover.Targets {
|
for _, targetID := range append([]string{primaryTargetID}, failover.Targets...) {
|
||||||
target := chain.Targets[failTargetID]
|
target := chain.Targets[targetID]
|
||||||
endpointGroup, valid := makeLoadAssignmentEndpointGroup(
|
clusterName := target.Name
|
||||||
chain.Targets,
|
targetUID := proxycfg.NewUpstreamIDFromTargetID(targetID)
|
||||||
upstreamEndpoints,
|
if targetUID.Peer != "" {
|
||||||
gatewayEndpoints,
|
tbs, ok := upstreamsSnapshot.UpstreamPeerTrustBundles.Get(targetUID.Peer)
|
||||||
failTargetID,
|
// We can't generate cluster on peers without the trust bundle. The
|
||||||
gatewayKey,
|
// trust bundle should be ready soon.
|
||||||
forMeshGateway,
|
if !ok {
|
||||||
)
|
s.Logger.Debug("peer trust bundle not ready for discovery chain target",
|
||||||
if !valid {
|
"peer", targetUID.Peer,
|
||||||
continue // skip the failover target if we're still populating the snapshot
|
"target", targetID,
|
||||||
}
|
)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
clusterName := CustomizeClusterName(target.Name, chain)
|
clusterName = generatePeeredClusterName(targetUID, tbs)
|
||||||
|
}
|
||||||
|
clusterName = CustomizeClusterName(clusterName, chain)
|
||||||
clusterName = failoverClusterNamePrefix + clusterName
|
clusterName = failoverClusterNamePrefix + clusterName
|
||||||
if escapeHatchCluster != nil {
|
if escapeHatchCluster != nil {
|
||||||
clusterName = escapeHatchCluster.Name
|
clusterName = escapeHatchCluster.Name
|
||||||
}
|
}
|
||||||
|
|
||||||
s.Logger.Debug("generating endpoints for", "cluster", clusterName)
|
targetLoadAssignmentOptions = append(targetLoadAssignmentOptions, targetLoadAssignmentOption{
|
||||||
|
targetID: targetID,
|
||||||
la := makeLoadAssignment(
|
clusterName: clusterName,
|
||||||
clusterName,
|
})
|
||||||
[]loadAssignmentEndpointGroup{endpointGroup},
|
|
||||||
gatewayKey,
|
|
||||||
)
|
|
||||||
resources = append(resources, la)
|
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
targetID := node.Resolver.Target
|
target := chain.Targets[primaryTargetID]
|
||||||
|
clusterName := CustomizeClusterName(target.Name, chain)
|
||||||
target := chain.Targets[targetID]
|
clusterName = clusterNamePrefix + clusterName
|
||||||
clusterName := CustomizeClusterName(target.Name, chain)
|
if escapeHatchCluster != nil {
|
||||||
clusterName = clusterNamePrefix + clusterName
|
clusterName = escapeHatchCluster.Name
|
||||||
if escapeHatchCluster != nil {
|
}
|
||||||
clusterName = escapeHatchCluster.Name
|
if forMeshGateway {
|
||||||
}
|
clusterName = meshGatewayExportedClusterNamePrefix + clusterName
|
||||||
if forMeshGateway {
|
}
|
||||||
clusterName = meshGatewayExportedClusterNamePrefix + clusterName
|
targetLoadAssignmentOptions = append(targetLoadAssignmentOptions, targetLoadAssignmentOption{
|
||||||
}
|
targetID: primaryTargetID,
|
||||||
s.Logger.Debug("generating endpoints for", "cluster", clusterName)
|
clusterName: clusterName,
|
||||||
endpointGroup, valid := makeLoadAssignmentEndpointGroup(
|
})
|
||||||
chain.Targets,
|
|
||||||
upstreamEndpoints,
|
|
||||||
gatewayEndpoints,
|
|
||||||
targetID,
|
|
||||||
gatewayKey,
|
|
||||||
forMeshGateway,
|
|
||||||
)
|
|
||||||
if !valid {
|
|
||||||
continue // skip the cluster if we're still populating the snapshot
|
|
||||||
}
|
}
|
||||||
|
|
||||||
la := makeLoadAssignment(
|
for _, targetInfo := range targetLoadAssignmentOptions {
|
||||||
clusterName,
|
s.Logger.Debug("generating endpoints for", "cluster", targetInfo.clusterName)
|
||||||
[]loadAssignmentEndpointGroup{endpointGroup},
|
targetUID := proxycfg.NewUpstreamIDFromTargetID(targetInfo.targetID)
|
||||||
gatewayKey,
|
if targetUID.Peer != "" {
|
||||||
)
|
loadAssignment, err := s.makeUpstreamLoadAssignmentForPeerService(cfgSnap, targetInfo.clusterName, targetUID)
|
||||||
resources = append(resources, la)
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if loadAssignment != nil {
|
||||||
|
resources = append(resources, loadAssignment)
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
endpointGroup, valid := makeLoadAssignmentEndpointGroup(
|
||||||
|
chain.Targets,
|
||||||
|
upstreamEndpoints,
|
||||||
|
gatewayEndpoints,
|
||||||
|
targetInfo.targetID,
|
||||||
|
gatewayKey,
|
||||||
|
forMeshGateway,
|
||||||
|
)
|
||||||
|
if !valid {
|
||||||
|
continue // skip the cluster if we're still populating the snapshot
|
||||||
|
}
|
||||||
|
|
||||||
|
la := makeLoadAssignment(
|
||||||
|
targetInfo.clusterName,
|
||||||
|
[]loadAssignmentEndpointGroup{endpointGroup},
|
||||||
|
gatewayKey,
|
||||||
|
)
|
||||||
|
resources = append(resources, la)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return resources, nil
|
return resources, nil
|
||||||
|
@ -586,6 +644,7 @@ func (s *ResourceGenerator) makeExportedUpstreamEndpointsForMeshGateway(cfgSnap
|
||||||
clusterEndpoints, err := s.endpointsFromDiscoveryChain(
|
clusterEndpoints, err := s.endpointsFromDiscoveryChain(
|
||||||
proxycfg.NewUpstreamIDFromServiceName(svc),
|
proxycfg.NewUpstreamIDFromServiceName(svc),
|
||||||
chain,
|
chain,
|
||||||
|
cfgSnap,
|
||||||
cfgSnap.Locality,
|
cfgSnap.Locality,
|
||||||
nil,
|
nil,
|
||||||
chainEndpoints,
|
chainEndpoints,
|
||||||
|
@ -640,11 +699,12 @@ func makeLoadAssignment(clusterName string, endpointGroups []loadAssignmentEndpo
|
||||||
healthStatus = endpointGroup.OverrideHealth
|
healthStatus = endpointGroup.OverrideHealth
|
||||||
}
|
}
|
||||||
|
|
||||||
|
endpoint := &envoy_endpoint_v3.Endpoint{
|
||||||
|
Address: makeAddress(addr, port),
|
||||||
|
}
|
||||||
es = append(es, &envoy_endpoint_v3.LbEndpoint{
|
es = append(es, &envoy_endpoint_v3.LbEndpoint{
|
||||||
HostIdentifier: &envoy_endpoint_v3.LbEndpoint_Endpoint{
|
HostIdentifier: &envoy_endpoint_v3.LbEndpoint_Endpoint{
|
||||||
Endpoint: &envoy_endpoint_v3.Endpoint{
|
Endpoint: endpoint,
|
||||||
Address: makeAddress(addr, port),
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
HealthStatus: healthStatus,
|
HealthStatus: healthStatus,
|
||||||
LoadBalancingWeight: makeUint32Value(weight),
|
LoadBalancingWeight: makeUint32Value(weight),
|
||||||
|
|
|
@ -284,6 +284,12 @@ func TestEndpointsFromSnapshot(t *testing.T) {
|
||||||
return proxycfg.TestConfigSnapshotDiscoveryChain(t, "failover", nil, nil)
|
return proxycfg.TestConfigSnapshotDiscoveryChain(t, "failover", nil, nil)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "connect-proxy-with-chain-and-failover-to-cluster-peer",
|
||||||
|
create: func(t testinf.T) *proxycfg.ConfigSnapshot {
|
||||||
|
return proxycfg.TestConfigSnapshotDiscoveryChain(t, "failover-to-cluster-peer", nil, nil)
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "connect-proxy-with-tcp-chain-failover-through-remote-gateway",
|
name: "connect-proxy-with-tcp-chain-failover-through-remote-gateway",
|
||||||
create: func(t testinf.T) *proxycfg.ConfigSnapshot {
|
create: func(t testinf.T) *proxycfg.ConfigSnapshot {
|
||||||
|
@ -396,6 +402,13 @@ func TestEndpointsFromSnapshot(t *testing.T) {
|
||||||
"failover", nil, nil, nil)
|
"failover", nil, nil, nil)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "ingress-with-chain-and-failover-to-cluster-peer",
|
||||||
|
create: func(t testinf.T) *proxycfg.ConfigSnapshot {
|
||||||
|
return proxycfg.TestConfigSnapshotIngressGateway(t, true, "tcp",
|
||||||
|
"failover-to-cluster-peer", nil, nil, nil)
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "ingress-with-tcp-chain-failover-through-remote-gateway",
|
name: "ingress-with-tcp-chain-failover-through-remote-gateway",
|
||||||
create: func(t testinf.T) *proxycfg.ConfigSnapshot {
|
create: func(t testinf.T) *proxycfg.ConfigSnapshot {
|
||||||
|
|
219
agent/xds/testdata/clusters/connect-proxy-with-chain-and-failover-to-cluster-peer.latest.golden
vendored
Normal file
219
agent/xds/testdata/clusters/connect-proxy-with-chain-and-failover-to-cluster-peer.latest.golden
vendored
Normal file
|
@ -0,0 +1,219 @@
|
||||||
|
{
|
||||||
|
"versionInfo": "00000001",
|
||||||
|
"resources": [
|
||||||
|
{
|
||||||
|
"@type": "type.googleapis.com/envoy.config.cluster.v3.Cluster",
|
||||||
|
"name": "db.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",
|
||||||
|
"altStatName": "db.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",
|
||||||
|
"clusterType": {
|
||||||
|
"name": "envoy.clusters.aggregate",
|
||||||
|
"typedConfig": {
|
||||||
|
"@type": "type.googleapis.com/envoy.extensions.clusters.aggregate.v3.ClusterConfig",
|
||||||
|
"clusters": [
|
||||||
|
"failover-target~db.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",
|
||||||
|
"failover-target~db.default.cluster-01.external.peer1.domain"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"connectTimeout": "33s",
|
||||||
|
"lbPolicy": "CLUSTER_PROVIDED"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"@type": "type.googleapis.com/envoy.config.cluster.v3.Cluster",
|
||||||
|
"name": "failover-target~db.default.cluster-01.external.peer1.domain",
|
||||||
|
"type": "EDS",
|
||||||
|
"edsClusterConfig": {
|
||||||
|
"edsConfig": {
|
||||||
|
"ads": {
|
||||||
|
|
||||||
|
},
|
||||||
|
"resourceApiVersion": "V3"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"connectTimeout": "1s",
|
||||||
|
"circuitBreakers": {
|
||||||
|
|
||||||
|
},
|
||||||
|
"outlierDetection": {
|
||||||
|
"maxEjectionPercent": 100
|
||||||
|
},
|
||||||
|
"commonLbConfig": {
|
||||||
|
"healthyPanicThreshold": {
|
||||||
|
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"transportSocket": {
|
||||||
|
"name": "tls",
|
||||||
|
"typedConfig": {
|
||||||
|
"@type": "type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext",
|
||||||
|
"commonTlsContext": {
|
||||||
|
"tlsParams": {
|
||||||
|
|
||||||
|
},
|
||||||
|
"tlsCertificates": [
|
||||||
|
{
|
||||||
|
"certificateChain": {
|
||||||
|
"inlineString": "-----BEGIN CERTIFICATE-----\nMIICjDCCAjKgAwIBAgIIC5llxGV1gB8wCgYIKoZIzj0EAwIwFDESMBAGA1UEAxMJ\nVGVzdCBDQSAyMB4XDTE5MDMyMjEzNTgyNloXDTI5MDMyMjEzNTgyNlowDjEMMAoG\nA1UEAxMDd2ViMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEADPv1RHVNRfa2VKR\nAB16b6rZnEt7tuhaxCFpQXPj7M2omb0B9Favq5E0ivpNtv1QnFhxtPd7d5k4e+T7\nSkW1TaOCAXIwggFuMA4GA1UdDwEB/wQEAwIDuDAdBgNVHSUEFjAUBggrBgEFBQcD\nAgYIKwYBBQUHAwEwDAYDVR0TAQH/BAIwADBoBgNVHQ4EYQRfN2Q6MDc6ODc6M2E6\nNDA6MTk6NDc6YzM6NWE6YzA6YmE6NjI6ZGY6YWY6NGI6ZDQ6MDU6MjU6NzY6M2Q6\nNWE6OGQ6MTY6OGQ6Njc6NWU6MmU6YTA6MzQ6N2Q6ZGM6ZmYwagYDVR0jBGMwYYBf\nZDE6MTE6MTE6YWM6MmE6YmE6OTc6YjI6M2Y6YWM6N2I6YmQ6ZGE6YmU6YjE6OGE6\nZmM6OWE6YmE6YjU6YmM6ODM6ZTc6NWU6NDE6NmY6ZjI6NzM6OTU6NTg6MGM6ZGIw\nWQYDVR0RBFIwUIZOc3BpZmZlOi8vMTExMTExMTEtMjIyMi0zMzMzLTQ0NDQtNTU1\nNTU1NTU1NTU1LmNvbnN1bC9ucy9kZWZhdWx0L2RjL2RjMS9zdmMvd2ViMAoGCCqG\nSM49BAMCA0gAMEUCIGC3TTvvjj76KMrguVyFf4tjOqaSCRie3nmHMRNNRav7AiEA\npY0heYeK9A6iOLrzqxSerkXXQyj5e9bE4VgUnxgPU6g=\n-----END CERTIFICATE-----\n"
|
||||||
|
},
|
||||||
|
"privateKey": {
|
||||||
|
"inlineString": "-----BEGIN EC PRIVATE KEY-----\nMHcCAQEEIMoTkpRggp3fqZzFKh82yS4LjtJI+XY+qX/7DefHFrtdoAoGCCqGSM49\nAwEHoUQDQgAEADPv1RHVNRfa2VKRAB16b6rZnEt7tuhaxCFpQXPj7M2omb0B9Fav\nq5E0ivpNtv1QnFhxtPd7d5k4e+T7SkW1TQ==\n-----END EC PRIVATE KEY-----\n"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"validationContext": {
|
||||||
|
"trustedCa": {
|
||||||
|
"inlineString": "peer1-root-1\n"
|
||||||
|
},
|
||||||
|
"matchSubjectAltNames": [
|
||||||
|
{
|
||||||
|
"exact": "spiffe://1c053652-8512-4373-90cf-5a7f6263a994.consul/ns/default/dc/cluster-01-dc/svc/payments"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"sni": "payments.default.default.cluster-01.external.1c053652-8512-4373-90cf-5a7f6263a994.consul"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"@type": "type.googleapis.com/envoy.config.cluster.v3.Cluster",
|
||||||
|
"name": "failover-target~db.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",
|
||||||
|
"altStatName": "failover-target~db.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",
|
||||||
|
"type": "EDS",
|
||||||
|
"edsClusterConfig": {
|
||||||
|
"edsConfig": {
|
||||||
|
"ads": {
|
||||||
|
|
||||||
|
},
|
||||||
|
"resourceApiVersion": "V3"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"connectTimeout": "33s",
|
||||||
|
"circuitBreakers": {
|
||||||
|
|
||||||
|
},
|
||||||
|
"outlierDetection": {
|
||||||
|
|
||||||
|
},
|
||||||
|
"commonLbConfig": {
|
||||||
|
"healthyPanicThreshold": {
|
||||||
|
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"transportSocket": {
|
||||||
|
"name": "tls",
|
||||||
|
"typedConfig": {
|
||||||
|
"@type": "type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext",
|
||||||
|
"commonTlsContext": {
|
||||||
|
"tlsParams": {
|
||||||
|
|
||||||
|
},
|
||||||
|
"tlsCertificates": [
|
||||||
|
{
|
||||||
|
"certificateChain": {
|
||||||
|
"inlineString": "-----BEGIN CERTIFICATE-----\nMIICjDCCAjKgAwIBAgIIC5llxGV1gB8wCgYIKoZIzj0EAwIwFDESMBAGA1UEAxMJ\nVGVzdCBDQSAyMB4XDTE5MDMyMjEzNTgyNloXDTI5MDMyMjEzNTgyNlowDjEMMAoG\nA1UEAxMDd2ViMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEADPv1RHVNRfa2VKR\nAB16b6rZnEt7tuhaxCFpQXPj7M2omb0B9Favq5E0ivpNtv1QnFhxtPd7d5k4e+T7\nSkW1TaOCAXIwggFuMA4GA1UdDwEB/wQEAwIDuDAdBgNVHSUEFjAUBggrBgEFBQcD\nAgYIKwYBBQUHAwEwDAYDVR0TAQH/BAIwADBoBgNVHQ4EYQRfN2Q6MDc6ODc6M2E6\nNDA6MTk6NDc6YzM6NWE6YzA6YmE6NjI6ZGY6YWY6NGI6ZDQ6MDU6MjU6NzY6M2Q6\nNWE6OGQ6MTY6OGQ6Njc6NWU6MmU6YTA6MzQ6N2Q6ZGM6ZmYwagYDVR0jBGMwYYBf\nZDE6MTE6MTE6YWM6MmE6YmE6OTc6YjI6M2Y6YWM6N2I6YmQ6ZGE6YmU6YjE6OGE6\nZmM6OWE6YmE6YjU6YmM6ODM6ZTc6NWU6NDE6NmY6ZjI6NzM6OTU6NTg6MGM6ZGIw\nWQYDVR0RBFIwUIZOc3BpZmZlOi8vMTExMTExMTEtMjIyMi0zMzMzLTQ0NDQtNTU1\nNTU1NTU1NTU1LmNvbnN1bC9ucy9kZWZhdWx0L2RjL2RjMS9zdmMvd2ViMAoGCCqG\nSM49BAMCA0gAMEUCIGC3TTvvjj76KMrguVyFf4tjOqaSCRie3nmHMRNNRav7AiEA\npY0heYeK9A6iOLrzqxSerkXXQyj5e9bE4VgUnxgPU6g=\n-----END CERTIFICATE-----\n"
|
||||||
|
},
|
||||||
|
"privateKey": {
|
||||||
|
"inlineString": "-----BEGIN EC PRIVATE KEY-----\nMHcCAQEEIMoTkpRggp3fqZzFKh82yS4LjtJI+XY+qX/7DefHFrtdoAoGCCqGSM49\nAwEHoUQDQgAEADPv1RHVNRfa2VKRAB16b6rZnEt7tuhaxCFpQXPj7M2omb0B9Fav\nq5E0ivpNtv1QnFhxtPd7d5k4e+T7SkW1TQ==\n-----END EC PRIVATE KEY-----\n"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"validationContext": {
|
||||||
|
"trustedCa": {
|
||||||
|
"inlineString": "-----BEGIN CERTIFICATE-----\nMIICXDCCAgKgAwIBAgIICpZq70Z9LyUwCgYIKoZIzj0EAwIwFDESMBAGA1UEAxMJ\nVGVzdCBDQSAyMB4XDTE5MDMyMjEzNTgyNloXDTI5MDMyMjEzNTgyNlowFDESMBAG\nA1UEAxMJVGVzdCBDQSAyMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEIhywH1gx\nAsMwuF3ukAI5YL2jFxH6Usnma1HFSfVyxbXX1/uoZEYrj8yCAtdU2yoHETyd+Zx2\nThhRLP79pYegCaOCATwwggE4MA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTAD\nAQH/MGgGA1UdDgRhBF9kMToxMToxMTphYzoyYTpiYTo5NzpiMjozZjphYzo3Yjpi\nZDpkYTpiZTpiMTo4YTpmYzo5YTpiYTpiNTpiYzo4MzplNzo1ZTo0MTo2ZjpmMjo3\nMzo5NTo1ODowYzpkYjBqBgNVHSMEYzBhgF9kMToxMToxMTphYzoyYTpiYTo5Nzpi\nMjozZjphYzo3YjpiZDpkYTpiZTpiMTo4YTpmYzo5YTpiYTpiNTpiYzo4MzplNzo1\nZTo0MTo2ZjpmMjo3Mzo5NTo1ODowYzpkYjA/BgNVHREEODA2hjRzcGlmZmU6Ly8x\nMTExMTExMS0yMjIyLTMzMzMtNDQ0NC01NTU1NTU1NTU1NTUuY29uc3VsMAoGCCqG\nSM49BAMCA0gAMEUCICOY0i246rQHJt8o8Oya0D5PLL1FnmsQmQqIGCi31RwnAiEA\noR5f6Ku+cig2Il8T8LJujOp2/2A72QcHZA57B13y+8o=\n-----END CERTIFICATE-----\n"
|
||||||
|
},
|
||||||
|
"matchSubjectAltNames": [
|
||||||
|
{
|
||||||
|
"exact": "spiffe://11111111-2222-3333-4444-555555555555.consul/ns/default/dc/dc1/svc/db"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"sni": "db.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"@type": "type.googleapis.com/envoy.config.cluster.v3.Cluster",
|
||||||
|
"name": "geo-cache.default.dc1.query.11111111-2222-3333-4444-555555555555.consul",
|
||||||
|
"type": "EDS",
|
||||||
|
"edsClusterConfig": {
|
||||||
|
"edsConfig": {
|
||||||
|
"ads": {
|
||||||
|
|
||||||
|
},
|
||||||
|
"resourceApiVersion": "V3"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"connectTimeout": "5s",
|
||||||
|
"circuitBreakers": {
|
||||||
|
|
||||||
|
},
|
||||||
|
"outlierDetection": {
|
||||||
|
|
||||||
|
},
|
||||||
|
"transportSocket": {
|
||||||
|
"name": "tls",
|
||||||
|
"typedConfig": {
|
||||||
|
"@type": "type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext",
|
||||||
|
"commonTlsContext": {
|
||||||
|
"tlsParams": {
|
||||||
|
|
||||||
|
},
|
||||||
|
"tlsCertificates": [
|
||||||
|
{
|
||||||
|
"certificateChain": {
|
||||||
|
"inlineString": "-----BEGIN CERTIFICATE-----\nMIICjDCCAjKgAwIBAgIIC5llxGV1gB8wCgYIKoZIzj0EAwIwFDESMBAGA1UEAxMJ\nVGVzdCBDQSAyMB4XDTE5MDMyMjEzNTgyNloXDTI5MDMyMjEzNTgyNlowDjEMMAoG\nA1UEAxMDd2ViMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEADPv1RHVNRfa2VKR\nAB16b6rZnEt7tuhaxCFpQXPj7M2omb0B9Favq5E0ivpNtv1QnFhxtPd7d5k4e+T7\nSkW1TaOCAXIwggFuMA4GA1UdDwEB/wQEAwIDuDAdBgNVHSUEFjAUBggrBgEFBQcD\nAgYIKwYBBQUHAwEwDAYDVR0TAQH/BAIwADBoBgNVHQ4EYQRfN2Q6MDc6ODc6M2E6\nNDA6MTk6NDc6YzM6NWE6YzA6YmE6NjI6ZGY6YWY6NGI6ZDQ6MDU6MjU6NzY6M2Q6\nNWE6OGQ6MTY6OGQ6Njc6NWU6MmU6YTA6MzQ6N2Q6ZGM6ZmYwagYDVR0jBGMwYYBf\nZDE6MTE6MTE6YWM6MmE6YmE6OTc6YjI6M2Y6YWM6N2I6YmQ6ZGE6YmU6YjE6OGE6\nZmM6OWE6YmE6YjU6YmM6ODM6ZTc6NWU6NDE6NmY6ZjI6NzM6OTU6NTg6MGM6ZGIw\nWQYDVR0RBFIwUIZOc3BpZmZlOi8vMTExMTExMTEtMjIyMi0zMzMzLTQ0NDQtNTU1\nNTU1NTU1NTU1LmNvbnN1bC9ucy9kZWZhdWx0L2RjL2RjMS9zdmMvd2ViMAoGCCqG\nSM49BAMCA0gAMEUCIGC3TTvvjj76KMrguVyFf4tjOqaSCRie3nmHMRNNRav7AiEA\npY0heYeK9A6iOLrzqxSerkXXQyj5e9bE4VgUnxgPU6g=\n-----END CERTIFICATE-----\n"
|
||||||
|
},
|
||||||
|
"privateKey": {
|
||||||
|
"inlineString": "-----BEGIN EC PRIVATE KEY-----\nMHcCAQEEIMoTkpRggp3fqZzFKh82yS4LjtJI+XY+qX/7DefHFrtdoAoGCCqGSM49\nAwEHoUQDQgAEADPv1RHVNRfa2VKRAB16b6rZnEt7tuhaxCFpQXPj7M2omb0B9Fav\nq5E0ivpNtv1QnFhxtPd7d5k4e+T7SkW1TQ==\n-----END EC PRIVATE KEY-----\n"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"validationContext": {
|
||||||
|
"trustedCa": {
|
||||||
|
"inlineString": "-----BEGIN CERTIFICATE-----\nMIICXDCCAgKgAwIBAgIICpZq70Z9LyUwCgYIKoZIzj0EAwIwFDESMBAGA1UEAxMJ\nVGVzdCBDQSAyMB4XDTE5MDMyMjEzNTgyNloXDTI5MDMyMjEzNTgyNlowFDESMBAG\nA1UEAxMJVGVzdCBDQSAyMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEIhywH1gx\nAsMwuF3ukAI5YL2jFxH6Usnma1HFSfVyxbXX1/uoZEYrj8yCAtdU2yoHETyd+Zx2\nThhRLP79pYegCaOCATwwggE4MA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTAD\nAQH/MGgGA1UdDgRhBF9kMToxMToxMTphYzoyYTpiYTo5NzpiMjozZjphYzo3Yjpi\nZDpkYTpiZTpiMTo4YTpmYzo5YTpiYTpiNTpiYzo4MzplNzo1ZTo0MTo2ZjpmMjo3\nMzo5NTo1ODowYzpkYjBqBgNVHSMEYzBhgF9kMToxMToxMTphYzoyYTpiYTo5Nzpi\nMjozZjphYzo3YjpiZDpkYTpiZTpiMTo4YTpmYzo5YTpiYTpiNTpiYzo4MzplNzo1\nZTo0MTo2ZjpmMjo3Mzo5NTo1ODowYzpkYjA/BgNVHREEODA2hjRzcGlmZmU6Ly8x\nMTExMTExMS0yMjIyLTMzMzMtNDQ0NC01NTU1NTU1NTU1NTUuY29uc3VsMAoGCCqG\nSM49BAMCA0gAMEUCICOY0i246rQHJt8o8Oya0D5PLL1FnmsQmQqIGCi31RwnAiEA\noR5f6Ku+cig2Il8T8LJujOp2/2A72QcHZA57B13y+8o=\n-----END CERTIFICATE-----\n"
|
||||||
|
},
|
||||||
|
"matchSubjectAltNames": [
|
||||||
|
{
|
||||||
|
"exact": "spiffe://11111111-2222-3333-4444-555555555555.consul/ns/default/dc/dc1/svc/geo-cache-target"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"exact": "spiffe://11111111-2222-3333-4444-555555555555.consul/ns/default/dc/dc2/svc/geo-cache-target"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"sni": "geo-cache.default.dc1.query.11111111-2222-3333-4444-555555555555.consul"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"@type": "type.googleapis.com/envoy.config.cluster.v3.Cluster",
|
||||||
|
"name": "local_app",
|
||||||
|
"type": "STATIC",
|
||||||
|
"connectTimeout": "5s",
|
||||||
|
"loadAssignment": {
|
||||||
|
"clusterName": "local_app",
|
||||||
|
"endpoints": [
|
||||||
|
{
|
||||||
|
"lbEndpoints": [
|
||||||
|
{
|
||||||
|
"endpoint": {
|
||||||
|
"address": {
|
||||||
|
"socketAddress": {
|
||||||
|
"address": "127.0.0.1",
|
||||||
|
"portValue": 8080
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"typeUrl": "type.googleapis.com/envoy.config.cluster.v3.Cluster",
|
||||||
|
"nonce": "00000001"
|
||||||
|
}
|
|
@ -58,7 +58,7 @@
|
||||||
"dnsRefreshRate": "10s",
|
"dnsRefreshRate": "10s",
|
||||||
"dnsLookupFamily": "V4_ONLY",
|
"dnsLookupFamily": "V4_ONLY",
|
||||||
"outlierDetection": {
|
"outlierDetection": {
|
||||||
|
"maxEjectionPercent": 100
|
||||||
},
|
},
|
||||||
"commonLbConfig": {
|
"commonLbConfig": {
|
||||||
"healthyPanicThreshold": {
|
"healthyPanicThreshold": {
|
||||||
|
@ -115,7 +115,7 @@
|
||||||
|
|
||||||
},
|
},
|
||||||
"outlierDetection": {
|
"outlierDetection": {
|
||||||
|
"maxEjectionPercent": 100
|
||||||
},
|
},
|
||||||
"commonLbConfig": {
|
"commonLbConfig": {
|
||||||
"healthyPanicThreshold": {
|
"healthyPanicThreshold": {
|
||||||
|
|
139
agent/xds/testdata/clusters/ingress-with-chain-and-failover-to-cluster-peer.latest.golden
vendored
Normal file
139
agent/xds/testdata/clusters/ingress-with-chain-and-failover-to-cluster-peer.latest.golden
vendored
Normal file
|
@ -0,0 +1,139 @@
|
||||||
|
{
|
||||||
|
"versionInfo": "00000001",
|
||||||
|
"resources": [
|
||||||
|
{
|
||||||
|
"@type": "type.googleapis.com/envoy.config.cluster.v3.Cluster",
|
||||||
|
"name": "db.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",
|
||||||
|
"altStatName": "db.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",
|
||||||
|
"clusterType": {
|
||||||
|
"name": "envoy.clusters.aggregate",
|
||||||
|
"typedConfig": {
|
||||||
|
"@type": "type.googleapis.com/envoy.extensions.clusters.aggregate.v3.ClusterConfig",
|
||||||
|
"clusters": [
|
||||||
|
"failover-target~db.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",
|
||||||
|
"failover-target~db.default.cluster-01.external.peer1.domain"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"connectTimeout": "33s",
|
||||||
|
"lbPolicy": "CLUSTER_PROVIDED"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"@type": "type.googleapis.com/envoy.config.cluster.v3.Cluster",
|
||||||
|
"name": "failover-target~db.default.cluster-01.external.peer1.domain",
|
||||||
|
"type": "EDS",
|
||||||
|
"edsClusterConfig": {
|
||||||
|
"edsConfig": {
|
||||||
|
"ads": {
|
||||||
|
|
||||||
|
},
|
||||||
|
"resourceApiVersion": "V3"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"connectTimeout": "33s",
|
||||||
|
"circuitBreakers": {
|
||||||
|
|
||||||
|
},
|
||||||
|
"outlierDetection": {
|
||||||
|
"maxEjectionPercent": 100
|
||||||
|
},
|
||||||
|
"commonLbConfig": {
|
||||||
|
"healthyPanicThreshold": {
|
||||||
|
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"transportSocket": {
|
||||||
|
"name": "tls",
|
||||||
|
"typedConfig": {
|
||||||
|
"@type": "type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext",
|
||||||
|
"commonTlsContext": {
|
||||||
|
"tlsParams": {
|
||||||
|
|
||||||
|
},
|
||||||
|
"tlsCertificates": [
|
||||||
|
{
|
||||||
|
"certificateChain": {
|
||||||
|
"inlineString": "-----BEGIN CERTIFICATE-----\nMIICjDCCAjKgAwIBAgIIC5llxGV1gB8wCgYIKoZIzj0EAwIwFDESMBAGA1UEAxMJ\nVGVzdCBDQSAyMB4XDTE5MDMyMjEzNTgyNloXDTI5MDMyMjEzNTgyNlowDjEMMAoG\nA1UEAxMDd2ViMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEADPv1RHVNRfa2VKR\nAB16b6rZnEt7tuhaxCFpQXPj7M2omb0B9Favq5E0ivpNtv1QnFhxtPd7d5k4e+T7\nSkW1TaOCAXIwggFuMA4GA1UdDwEB/wQEAwIDuDAdBgNVHSUEFjAUBggrBgEFBQcD\nAgYIKwYBBQUHAwEwDAYDVR0TAQH/BAIwADBoBgNVHQ4EYQRfN2Q6MDc6ODc6M2E6\nNDA6MTk6NDc6YzM6NWE6YzA6YmE6NjI6ZGY6YWY6NGI6ZDQ6MDU6MjU6NzY6M2Q6\nNWE6OGQ6MTY6OGQ6Njc6NWU6MmU6YTA6MzQ6N2Q6ZGM6ZmYwagYDVR0jBGMwYYBf\nZDE6MTE6MTE6YWM6MmE6YmE6OTc6YjI6M2Y6YWM6N2I6YmQ6ZGE6YmU6YjE6OGE6\nZmM6OWE6YmE6YjU6YmM6ODM6ZTc6NWU6NDE6NmY6ZjI6NzM6OTU6NTg6MGM6ZGIw\nWQYDVR0RBFIwUIZOc3BpZmZlOi8vMTExMTExMTEtMjIyMi0zMzMzLTQ0NDQtNTU1\nNTU1NTU1NTU1LmNvbnN1bC9ucy9kZWZhdWx0L2RjL2RjMS9zdmMvd2ViMAoGCCqG\nSM49BAMCA0gAMEUCIGC3TTvvjj76KMrguVyFf4tjOqaSCRie3nmHMRNNRav7AiEA\npY0heYeK9A6iOLrzqxSerkXXQyj5e9bE4VgUnxgPU6g=\n-----END CERTIFICATE-----\n"
|
||||||
|
},
|
||||||
|
"privateKey": {
|
||||||
|
"inlineString": "-----BEGIN EC PRIVATE KEY-----\nMHcCAQEEIMoTkpRggp3fqZzFKh82yS4LjtJI+XY+qX/7DefHFrtdoAoGCCqGSM49\nAwEHoUQDQgAEADPv1RHVNRfa2VKRAB16b6rZnEt7tuhaxCFpQXPj7M2omb0B9Fav\nq5E0ivpNtv1QnFhxtPd7d5k4e+T7SkW1TQ==\n-----END EC PRIVATE KEY-----\n"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"validationContext": {
|
||||||
|
"trustedCa": {
|
||||||
|
"inlineString": "peer1-root-1\n"
|
||||||
|
},
|
||||||
|
"matchSubjectAltNames": [
|
||||||
|
{
|
||||||
|
"exact": "spiffe://1c053652-8512-4373-90cf-5a7f6263a994.consul/ns/default/dc/cluster-01-dc/svc/payments"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"sni": "payments.default.default.cluster-01.external.1c053652-8512-4373-90cf-5a7f6263a994.consul"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"@type": "type.googleapis.com/envoy.config.cluster.v3.Cluster",
|
||||||
|
"name": "failover-target~db.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",
|
||||||
|
"altStatName": "failover-target~db.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",
|
||||||
|
"type": "EDS",
|
||||||
|
"edsClusterConfig": {
|
||||||
|
"edsConfig": {
|
||||||
|
"ads": {
|
||||||
|
|
||||||
|
},
|
||||||
|
"resourceApiVersion": "V3"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"connectTimeout": "33s",
|
||||||
|
"circuitBreakers": {
|
||||||
|
|
||||||
|
},
|
||||||
|
"outlierDetection": {
|
||||||
|
|
||||||
|
},
|
||||||
|
"commonLbConfig": {
|
||||||
|
"healthyPanicThreshold": {
|
||||||
|
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"transportSocket": {
|
||||||
|
"name": "tls",
|
||||||
|
"typedConfig": {
|
||||||
|
"@type": "type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext",
|
||||||
|
"commonTlsContext": {
|
||||||
|
"tlsParams": {
|
||||||
|
|
||||||
|
},
|
||||||
|
"tlsCertificates": [
|
||||||
|
{
|
||||||
|
"certificateChain": {
|
||||||
|
"inlineString": "-----BEGIN CERTIFICATE-----\nMIICjDCCAjKgAwIBAgIIC5llxGV1gB8wCgYIKoZIzj0EAwIwFDESMBAGA1UEAxMJ\nVGVzdCBDQSAyMB4XDTE5MDMyMjEzNTgyNloXDTI5MDMyMjEzNTgyNlowDjEMMAoG\nA1UEAxMDd2ViMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEADPv1RHVNRfa2VKR\nAB16b6rZnEt7tuhaxCFpQXPj7M2omb0B9Favq5E0ivpNtv1QnFhxtPd7d5k4e+T7\nSkW1TaOCAXIwggFuMA4GA1UdDwEB/wQEAwIDuDAdBgNVHSUEFjAUBggrBgEFBQcD\nAgYIKwYBBQUHAwEwDAYDVR0TAQH/BAIwADBoBgNVHQ4EYQRfN2Q6MDc6ODc6M2E6\nNDA6MTk6NDc6YzM6NWE6YzA6YmE6NjI6ZGY6YWY6NGI6ZDQ6MDU6MjU6NzY6M2Q6\nNWE6OGQ6MTY6OGQ6Njc6NWU6MmU6YTA6MzQ6N2Q6ZGM6ZmYwagYDVR0jBGMwYYBf\nZDE6MTE6MTE6YWM6MmE6YmE6OTc6YjI6M2Y6YWM6N2I6YmQ6ZGE6YmU6YjE6OGE6\nZmM6OWE6YmE6YjU6YmM6ODM6ZTc6NWU6NDE6NmY6ZjI6NzM6OTU6NTg6MGM6ZGIw\nWQYDVR0RBFIwUIZOc3BpZmZlOi8vMTExMTExMTEtMjIyMi0zMzMzLTQ0NDQtNTU1\nNTU1NTU1NTU1LmNvbnN1bC9ucy9kZWZhdWx0L2RjL2RjMS9zdmMvd2ViMAoGCCqG\nSM49BAMCA0gAMEUCIGC3TTvvjj76KMrguVyFf4tjOqaSCRie3nmHMRNNRav7AiEA\npY0heYeK9A6iOLrzqxSerkXXQyj5e9bE4VgUnxgPU6g=\n-----END CERTIFICATE-----\n"
|
||||||
|
},
|
||||||
|
"privateKey": {
|
||||||
|
"inlineString": "-----BEGIN EC PRIVATE KEY-----\nMHcCAQEEIMoTkpRggp3fqZzFKh82yS4LjtJI+XY+qX/7DefHFrtdoAoGCCqGSM49\nAwEHoUQDQgAEADPv1RHVNRfa2VKRAB16b6rZnEt7tuhaxCFpQXPj7M2omb0B9Fav\nq5E0ivpNtv1QnFhxtPd7d5k4e+T7SkW1TQ==\n-----END EC PRIVATE KEY-----\n"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"validationContext": {
|
||||||
|
"trustedCa": {
|
||||||
|
"inlineString": "-----BEGIN CERTIFICATE-----\nMIICXDCCAgKgAwIBAgIICpZq70Z9LyUwCgYIKoZIzj0EAwIwFDESMBAGA1UEAxMJ\nVGVzdCBDQSAyMB4XDTE5MDMyMjEzNTgyNloXDTI5MDMyMjEzNTgyNlowFDESMBAG\nA1UEAxMJVGVzdCBDQSAyMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEIhywH1gx\nAsMwuF3ukAI5YL2jFxH6Usnma1HFSfVyxbXX1/uoZEYrj8yCAtdU2yoHETyd+Zx2\nThhRLP79pYegCaOCATwwggE4MA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTAD\nAQH/MGgGA1UdDgRhBF9kMToxMToxMTphYzoyYTpiYTo5NzpiMjozZjphYzo3Yjpi\nZDpkYTpiZTpiMTo4YTpmYzo5YTpiYTpiNTpiYzo4MzplNzo1ZTo0MTo2ZjpmMjo3\nMzo5NTo1ODowYzpkYjBqBgNVHSMEYzBhgF9kMToxMToxMTphYzoyYTpiYTo5Nzpi\nMjozZjphYzo3YjpiZDpkYTpiZTpiMTo4YTpmYzo5YTpiYTpiNTpiYzo4MzplNzo1\nZTo0MTo2ZjpmMjo3Mzo5NTo1ODowYzpkYjA/BgNVHREEODA2hjRzcGlmZmU6Ly8x\nMTExMTExMS0yMjIyLTMzMzMtNDQ0NC01NTU1NTU1NTU1NTUuY29uc3VsMAoGCCqG\nSM49BAMCA0gAMEUCICOY0i246rQHJt8o8Oya0D5PLL1FnmsQmQqIGCi31RwnAiEA\noR5f6Ku+cig2Il8T8LJujOp2/2A72QcHZA57B13y+8o=\n-----END CERTIFICATE-----\n"
|
||||||
|
},
|
||||||
|
"matchSubjectAltNames": [
|
||||||
|
{
|
||||||
|
"exact": "spiffe://11111111-2222-3333-4444-555555555555.consul/ns/default/dc/dc1/svc/db"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"sni": "db.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"typeUrl": "type.googleapis.com/envoy.config.cluster.v3.Cluster",
|
||||||
|
"nonce": "00000001"
|
||||||
|
}
|
|
@ -18,7 +18,7 @@
|
||||||
|
|
||||||
},
|
},
|
||||||
"outlierDetection": {
|
"outlierDetection": {
|
||||||
|
"maxEjectionPercent": 100
|
||||||
},
|
},
|
||||||
"commonLbConfig": {
|
"commonLbConfig": {
|
||||||
"healthyPanicThreshold": {
|
"healthyPanicThreshold": {
|
||||||
|
@ -75,7 +75,7 @@
|
||||||
|
|
||||||
},
|
},
|
||||||
"outlierDetection": {
|
"outlierDetection": {
|
||||||
|
"maxEjectionPercent": 100
|
||||||
},
|
},
|
||||||
"commonLbConfig": {
|
"commonLbConfig": {
|
||||||
"healthyPanicThreshold": {
|
"healthyPanicThreshold": {
|
||||||
|
@ -157,7 +157,7 @@
|
||||||
|
|
||||||
},
|
},
|
||||||
"outlierDetection": {
|
"outlierDetection": {
|
||||||
|
"maxEjectionPercent": 100
|
||||||
},
|
},
|
||||||
"commonLbConfig": {
|
"commonLbConfig": {
|
||||||
"healthyPanicThreshold": {
|
"healthyPanicThreshold": {
|
||||||
|
|
109
agent/xds/testdata/endpoints/connect-proxy-with-chain-and-failover-to-cluster-peer.latest.golden
vendored
Normal file
109
agent/xds/testdata/endpoints/connect-proxy-with-chain-and-failover-to-cluster-peer.latest.golden
vendored
Normal file
|
@ -0,0 +1,109 @@
|
||||||
|
{
|
||||||
|
"versionInfo": "00000001",
|
||||||
|
"resources": [
|
||||||
|
{
|
||||||
|
"@type": "type.googleapis.com/envoy.config.endpoint.v3.ClusterLoadAssignment",
|
||||||
|
"clusterName": "failover-target~db.default.cluster-01.external.peer1.domain",
|
||||||
|
"endpoints": [
|
||||||
|
{
|
||||||
|
"lbEndpoints": [
|
||||||
|
{
|
||||||
|
"endpoint": {
|
||||||
|
"address": {
|
||||||
|
"socketAddress": {
|
||||||
|
"address": "10.40.1.1",
|
||||||
|
"portValue": 8080
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"healthStatus": "HEALTHY",
|
||||||
|
"loadBalancingWeight": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"endpoint": {
|
||||||
|
"address": {
|
||||||
|
"socketAddress": {
|
||||||
|
"address": "10.40.1.2",
|
||||||
|
"portValue": 8080
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"healthStatus": "HEALTHY",
|
||||||
|
"loadBalancingWeight": 1
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"@type": "type.googleapis.com/envoy.config.endpoint.v3.ClusterLoadAssignment",
|
||||||
|
"clusterName": "failover-target~db.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",
|
||||||
|
"endpoints": [
|
||||||
|
{
|
||||||
|
"lbEndpoints": [
|
||||||
|
{
|
||||||
|
"endpoint": {
|
||||||
|
"address": {
|
||||||
|
"socketAddress": {
|
||||||
|
"address": "10.10.1.1",
|
||||||
|
"portValue": 8080
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"healthStatus": "HEALTHY",
|
||||||
|
"loadBalancingWeight": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"endpoint": {
|
||||||
|
"address": {
|
||||||
|
"socketAddress": {
|
||||||
|
"address": "10.10.1.2",
|
||||||
|
"portValue": 8080
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"healthStatus": "HEALTHY",
|
||||||
|
"loadBalancingWeight": 1
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"@type": "type.googleapis.com/envoy.config.endpoint.v3.ClusterLoadAssignment",
|
||||||
|
"clusterName": "geo-cache.default.dc1.query.11111111-2222-3333-4444-555555555555.consul",
|
||||||
|
"endpoints": [
|
||||||
|
{
|
||||||
|
"lbEndpoints": [
|
||||||
|
{
|
||||||
|
"endpoint": {
|
||||||
|
"address": {
|
||||||
|
"socketAddress": {
|
||||||
|
"address": "10.10.1.1",
|
||||||
|
"portValue": 8080
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"healthStatus": "HEALTHY",
|
||||||
|
"loadBalancingWeight": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"endpoint": {
|
||||||
|
"address": {
|
||||||
|
"socketAddress": {
|
||||||
|
"address": "10.20.1.2",
|
||||||
|
"portValue": 8080
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"healthStatus": "HEALTHY",
|
||||||
|
"loadBalancingWeight": 1
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"typeUrl": "type.googleapis.com/envoy.config.endpoint.v3.ClusterLoadAssignment",
|
||||||
|
"nonce": "00000001"
|
||||||
|
}
|
75
agent/xds/testdata/endpoints/ingress-with-chain-and-failover-to-cluster-peer.latest.golden
vendored
Normal file
75
agent/xds/testdata/endpoints/ingress-with-chain-and-failover-to-cluster-peer.latest.golden
vendored
Normal file
|
@ -0,0 +1,75 @@
|
||||||
|
{
|
||||||
|
"versionInfo": "00000001",
|
||||||
|
"resources": [
|
||||||
|
{
|
||||||
|
"@type": "type.googleapis.com/envoy.config.endpoint.v3.ClusterLoadAssignment",
|
||||||
|
"clusterName": "failover-target~db.default.cluster-01.external.peer1.domain",
|
||||||
|
"endpoints": [
|
||||||
|
{
|
||||||
|
"lbEndpoints": [
|
||||||
|
{
|
||||||
|
"endpoint": {
|
||||||
|
"address": {
|
||||||
|
"socketAddress": {
|
||||||
|
"address": "10.40.1.1",
|
||||||
|
"portValue": 8080
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"healthStatus": "HEALTHY",
|
||||||
|
"loadBalancingWeight": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"endpoint": {
|
||||||
|
"address": {
|
||||||
|
"socketAddress": {
|
||||||
|
"address": "10.40.1.2",
|
||||||
|
"portValue": 8080
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"healthStatus": "HEALTHY",
|
||||||
|
"loadBalancingWeight": 1
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"@type": "type.googleapis.com/envoy.config.endpoint.v3.ClusterLoadAssignment",
|
||||||
|
"clusterName": "failover-target~db.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",
|
||||||
|
"endpoints": [
|
||||||
|
{
|
||||||
|
"lbEndpoints": [
|
||||||
|
{
|
||||||
|
"endpoint": {
|
||||||
|
"address": {
|
||||||
|
"socketAddress": {
|
||||||
|
"address": "10.10.1.1",
|
||||||
|
"portValue": 8080
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"healthStatus": "HEALTHY",
|
||||||
|
"loadBalancingWeight": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"endpoint": {
|
||||||
|
"address": {
|
||||||
|
"socketAddress": {
|
||||||
|
"address": "10.10.1.2",
|
||||||
|
"portValue": 8080
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"healthStatus": "HEALTHY",
|
||||||
|
"loadBalancingWeight": 1
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"typeUrl": "type.googleapis.com/envoy.config.endpoint.v3.ClusterLoadAssignment",
|
||||||
|
"nonce": "00000001"
|
||||||
|
}
|
|
@ -219,6 +219,7 @@ type ServiceResolverRedirect struct {
|
||||||
Namespace string `json:",omitempty"`
|
Namespace string `json:",omitempty"`
|
||||||
Partition string `json:",omitempty"`
|
Partition string `json:",omitempty"`
|
||||||
Datacenter string `json:",omitempty"`
|
Datacenter string `json:",omitempty"`
|
||||||
|
Peer string `json:",omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type ServiceResolverFailover struct {
|
type ServiceResolverFailover struct {
|
||||||
|
|
|
@ -193,6 +193,20 @@ func TestAPI_ConfigEntry_DiscoveryChain(t *testing.T) {
|
||||||
},
|
},
|
||||||
verify: verifyResolver,
|
verify: verifyResolver,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "redirect to peer",
|
||||||
|
entry: &ServiceResolverConfigEntry{
|
||||||
|
Kind: ServiceResolver,
|
||||||
|
Name: "test-redirect",
|
||||||
|
Partition: splitDefaultPartition,
|
||||||
|
Namespace: splitDefaultNamespace,
|
||||||
|
Redirect: &ServiceResolverRedirect{
|
||||||
|
Service: "test-failover",
|
||||||
|
Peer: "cluster-01",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
verify: verifyResolver,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "mega splitter", // use one mega object to avoid multiple trips
|
name: "mega splitter", // use one mega object to avoid multiple trips
|
||||||
entry: &ServiceSplitterConfigEntry{
|
entry: &ServiceSplitterConfigEntry{
|
||||||
|
|
|
@ -17,6 +17,9 @@ type QueryFailoverOptions struct {
|
||||||
Targets []QueryFailoverTarget
|
Targets []QueryFailoverTarget
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Deprecated: use QueryFailoverOptions instead.
|
||||||
|
type QueryDatacenterOptions = QueryFailoverOptions
|
||||||
|
|
||||||
type QueryFailoverTarget struct {
|
type QueryFailoverTarget struct {
|
||||||
// PeerName specifies a peer to try during failover.
|
// PeerName specifies a peer to try during failover.
|
||||||
PeerName string
|
PeerName string
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
ARG CONSUL_IMAGE_VERSION=latest
|
||||||
|
FROM consul:${CONSUL_IMAGE_VERSION}
|
||||||
|
RUN apk update && apk add iptables
|
||||||
|
ARG TARGETARCH
|
||||||
|
COPY linux_${TARGETARCH}/consul /bin/consul
|
|
@ -689,6 +689,7 @@ func ServiceResolverRedirectToStructs(s *ServiceResolverRedirect, t *structs.Ser
|
||||||
t.Namespace = s.Namespace
|
t.Namespace = s.Namespace
|
||||||
t.Partition = s.Partition
|
t.Partition = s.Partition
|
||||||
t.Datacenter = s.Datacenter
|
t.Datacenter = s.Datacenter
|
||||||
|
t.Peer = s.Peer
|
||||||
}
|
}
|
||||||
func ServiceResolverRedirectFromStructs(t *structs.ServiceResolverRedirect, s *ServiceResolverRedirect) {
|
func ServiceResolverRedirectFromStructs(t *structs.ServiceResolverRedirect, s *ServiceResolverRedirect) {
|
||||||
if s == nil {
|
if s == nil {
|
||||||
|
@ -699,6 +700,7 @@ func ServiceResolverRedirectFromStructs(t *structs.ServiceResolverRedirect, s *S
|
||||||
s.Namespace = t.Namespace
|
s.Namespace = t.Namespace
|
||||||
s.Partition = t.Partition
|
s.Partition = t.Partition
|
||||||
s.Datacenter = t.Datacenter
|
s.Datacenter = t.Datacenter
|
||||||
|
s.Peer = t.Peer
|
||||||
}
|
}
|
||||||
func ServiceResolverSubsetToStructs(s *ServiceResolverSubset, t *structs.ServiceResolverSubset) {
|
func ServiceResolverSubsetToStructs(s *ServiceResolverSubset, t *structs.ServiceResolverSubset) {
|
||||||
if s == nil {
|
if s == nil {
|
||||||
|
|
|
@ -796,6 +796,7 @@ type ServiceResolverRedirect struct {
|
||||||
Namespace string `protobuf:"bytes,3,opt,name=Namespace,proto3" json:"Namespace,omitempty"`
|
Namespace string `protobuf:"bytes,3,opt,name=Namespace,proto3" json:"Namespace,omitempty"`
|
||||||
Partition string `protobuf:"bytes,4,opt,name=Partition,proto3" json:"Partition,omitempty"`
|
Partition string `protobuf:"bytes,4,opt,name=Partition,proto3" json:"Partition,omitempty"`
|
||||||
Datacenter string `protobuf:"bytes,5,opt,name=Datacenter,proto3" json:"Datacenter,omitempty"`
|
Datacenter string `protobuf:"bytes,5,opt,name=Datacenter,proto3" json:"Datacenter,omitempty"`
|
||||||
|
Peer string `protobuf:"bytes,6,opt,name=Peer,proto3" json:"Peer,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *ServiceResolverRedirect) Reset() {
|
func (x *ServiceResolverRedirect) Reset() {
|
||||||
|
@ -865,6 +866,13 @@ func (x *ServiceResolverRedirect) GetDatacenter() string {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (x *ServiceResolverRedirect) GetPeer() string {
|
||||||
|
if x != nil {
|
||||||
|
return x.Peer
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
// mog annotation:
|
// mog annotation:
|
||||||
//
|
//
|
||||||
// target=github.com/hashicorp/consul/agent/structs.ServiceResolverFailover
|
// target=github.com/hashicorp/consul/agent/structs.ServiceResolverFailover
|
||||||
|
@ -2521,7 +2529,7 @@ var file_proto_pbconfigentry_config_entry_proto_rawDesc = []byte{
|
||||||
0x65, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01,
|
0x65, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01,
|
||||||
0x28, 0x09, 0x52, 0x06, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x12, 0x20, 0x0a, 0x0b, 0x4f, 0x6e,
|
0x28, 0x09, 0x52, 0x06, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x12, 0x20, 0x0a, 0x0b, 0x4f, 0x6e,
|
||||||
0x6c, 0x79, 0x50, 0x61, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52,
|
0x6c, 0x79, 0x50, 0x61, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52,
|
||||||
0x0b, 0x4f, 0x6e, 0x6c, 0x79, 0x50, 0x61, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x22, 0xb5, 0x01, 0x0a,
|
0x0b, 0x4f, 0x6e, 0x6c, 0x79, 0x50, 0x61, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x22, 0xc9, 0x01, 0x0a,
|
||||||
0x17, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x72,
|
0x17, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x72,
|
||||||
0x52, 0x65, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x53, 0x65, 0x72, 0x76,
|
0x52, 0x65, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x53, 0x65, 0x72, 0x76,
|
||||||
0x69, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x53, 0x65, 0x72, 0x76, 0x69,
|
0x69, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x53, 0x65, 0x72, 0x76, 0x69,
|
||||||
|
@ -2533,341 +2541,342 @@ var file_proto_pbconfigentry_config_entry_proto_rawDesc = []byte{
|
||||||
0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x50, 0x61, 0x72, 0x74, 0x69,
|
0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x50, 0x61, 0x72, 0x74, 0x69,
|
||||||
0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1e, 0x0a, 0x0a, 0x44, 0x61, 0x74, 0x61, 0x63, 0x65, 0x6e, 0x74,
|
0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1e, 0x0a, 0x0a, 0x44, 0x61, 0x74, 0x61, 0x63, 0x65, 0x6e, 0x74,
|
||||||
0x65, 0x72, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x44, 0x61, 0x74, 0x61, 0x63, 0x65,
|
0x65, 0x72, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x44, 0x61, 0x74, 0x61, 0x63, 0x65,
|
||||||
0x6e, 0x74, 0x65, 0x72, 0x22, 0xf9, 0x01, 0x0a, 0x17, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65,
|
0x6e, 0x74, 0x65, 0x72, 0x12, 0x12, 0x0a, 0x04, 0x50, 0x65, 0x65, 0x72, 0x18, 0x06, 0x20, 0x01,
|
||||||
0x52, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x72, 0x46, 0x61, 0x69, 0x6c, 0x6f, 0x76, 0x65, 0x72,
|
0x28, 0x09, 0x52, 0x04, 0x50, 0x65, 0x65, 0x72, 0x22, 0xf9, 0x01, 0x0a, 0x17, 0x53, 0x65, 0x72,
|
||||||
0x12, 0x18, 0x0a, 0x07, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28,
|
0x76, 0x69, 0x63, 0x65, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x72, 0x46, 0x61, 0x69, 0x6c,
|
||||||
0x09, 0x52, 0x07, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x24, 0x0a, 0x0d, 0x53, 0x65,
|
0x6f, 0x76, 0x65, 0x72, 0x12, 0x18, 0x0a, 0x07, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x18,
|
||||||
0x72, 0x76, 0x69, 0x63, 0x65, 0x53, 0x75, 0x62, 0x73, 0x65, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28,
|
0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x24,
|
||||||
0x09, 0x52, 0x0d, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x53, 0x75, 0x62, 0x73, 0x65, 0x74,
|
0x0a, 0x0d, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x53, 0x75, 0x62, 0x73, 0x65, 0x74, 0x18,
|
||||||
0x12, 0x1c, 0x0a, 0x09, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x03, 0x20,
|
0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x53, 0x75,
|
||||||
0x01, 0x28, 0x09, 0x52, 0x09, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x20,
|
0x62, 0x73, 0x65, 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63,
|
||||||
0x0a, 0x0b, 0x44, 0x61, 0x74, 0x61, 0x63, 0x65, 0x6e, 0x74, 0x65, 0x72, 0x73, 0x18, 0x04, 0x20,
|
0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61,
|
||||||
0x03, 0x28, 0x09, 0x52, 0x0b, 0x44, 0x61, 0x74, 0x61, 0x63, 0x65, 0x6e, 0x74, 0x65, 0x72, 0x73,
|
0x63, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x44, 0x61, 0x74, 0x61, 0x63, 0x65, 0x6e, 0x74, 0x65, 0x72,
|
||||||
0x12, 0x5e, 0x0a, 0x07, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28,
|
0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0b, 0x44, 0x61, 0x74, 0x61, 0x63, 0x65, 0x6e,
|
||||||
0x0b, 0x32, 0x44, 0x2e, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x63, 0x6f,
|
0x74, 0x65, 0x72, 0x73, 0x12, 0x5e, 0x0a, 0x07, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x73, 0x18,
|
||||||
0x6e, 0x73, 0x75, 0x6c, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2e, 0x63, 0x6f,
|
0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x44, 0x2e, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72,
|
||||||
0x6e, 0x66, 0x69, 0x67, 0x65, 0x6e, 0x74, 0x72, 0x79, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63,
|
|
||||||
0x65, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x72, 0x46, 0x61, 0x69, 0x6c, 0x6f, 0x76, 0x65,
|
|
||||||
0x72, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x52, 0x07, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x73,
|
|
||||||
0x22, 0xcf, 0x01, 0x0a, 0x1d, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x52, 0x65, 0x73, 0x6f,
|
|
||||||
0x6c, 0x76, 0x65, 0x72, 0x46, 0x61, 0x69, 0x6c, 0x6f, 0x76, 0x65, 0x72, 0x54, 0x61, 0x72, 0x67,
|
|
||||||
0x65, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x18, 0x01, 0x20,
|
|
||||||
0x01, 0x28, 0x09, 0x52, 0x07, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x24, 0x0a, 0x0d,
|
|
||||||
0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x53, 0x75, 0x62, 0x73, 0x65, 0x74, 0x18, 0x02, 0x20,
|
|
||||||
0x01, 0x28, 0x09, 0x52, 0x0d, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x53, 0x75, 0x62, 0x73,
|
|
||||||
0x65, 0x74, 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, 0x1c, 0x0a, 0x09, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x04, 0x20,
|
|
||||||
0x01, 0x28, 0x09, 0x52, 0x09, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x1e,
|
|
||||||
0x0a, 0x0a, 0x44, 0x61, 0x74, 0x61, 0x63, 0x65, 0x6e, 0x74, 0x65, 0x72, 0x18, 0x05, 0x20, 0x01,
|
|
||||||
0x28, 0x09, 0x52, 0x0a, 0x44, 0x61, 0x74, 0x61, 0x63, 0x65, 0x6e, 0x74, 0x65, 0x72, 0x12, 0x12,
|
|
||||||
0x0a, 0x04, 0x50, 0x65, 0x65, 0x72, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x50, 0x65,
|
|
||||||
0x65, 0x72, 0x22, 0xc7, 0x02, 0x0a, 0x0c, 0x4c, 0x6f, 0x61, 0x64, 0x42, 0x61, 0x6c, 0x61, 0x6e,
|
|
||||||
0x63, 0x65, 0x72, 0x12, 0x16, 0x0a, 0x06, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x18, 0x01, 0x20,
|
|
||||||
0x01, 0x28, 0x09, 0x52, 0x06, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x12, 0x5d, 0x0a, 0x0e, 0x52,
|
|
||||||
0x69, 0x6e, 0x67, 0x48, 0x61, 0x73, 0x68, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 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,
|
|
||||||
0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x65, 0x6e, 0x74, 0x72, 0x79, 0x2e, 0x52, 0x69, 0x6e, 0x67,
|
|
||||||
0x48, 0x61, 0x73, 0x68, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x0e, 0x52, 0x69, 0x6e, 0x67,
|
|
||||||
0x48, 0x61, 0x73, 0x68, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x69, 0x0a, 0x12, 0x4c, 0x65,
|
|
||||||
0x61, 0x73, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67,
|
|
||||||
0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 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, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x65, 0x6e, 0x74, 0x72, 0x79, 0x2e, 0x4c,
|
|
||||||
0x65, 0x61, 0x73, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69,
|
|
||||||
0x67, 0x52, 0x12, 0x4c, 0x65, 0x61, 0x73, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x43,
|
|
||||||
0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x55, 0x0a, 0x0c, 0x48, 0x61, 0x73, 0x68, 0x50, 0x6f, 0x6c,
|
|
||||||
0x69, 0x63, 0x69, 0x65, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x31, 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, 0x66, 0x69, 0x67, 0x65, 0x6e,
|
|
||||||
0x74, 0x72, 0x79, 0x2e, 0x48, 0x61, 0x73, 0x68, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52, 0x0c,
|
|
||||||
0x48, 0x61, 0x73, 0x68, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x69, 0x65, 0x73, 0x22, 0x64, 0x0a, 0x0e,
|
|
||||||
0x52, 0x69, 0x6e, 0x67, 0x48, 0x61, 0x73, 0x68, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x28,
|
|
||||||
0x0a, 0x0f, 0x4d, 0x69, 0x6e, 0x69, 0x6d, 0x75, 0x6d, 0x52, 0x69, 0x6e, 0x67, 0x53, 0x69, 0x7a,
|
|
||||||
0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0f, 0x4d, 0x69, 0x6e, 0x69, 0x6d, 0x75, 0x6d,
|
|
||||||
0x52, 0x69, 0x6e, 0x67, 0x53, 0x69, 0x7a, 0x65, 0x12, 0x28, 0x0a, 0x0f, 0x4d, 0x61, 0x78, 0x69,
|
|
||||||
0x6d, 0x75, 0x6d, 0x52, 0x69, 0x6e, 0x67, 0x53, 0x69, 0x7a, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28,
|
|
||||||
0x04, 0x52, 0x0f, 0x4d, 0x61, 0x78, 0x69, 0x6d, 0x75, 0x6d, 0x52, 0x69, 0x6e, 0x67, 0x53, 0x69,
|
|
||||||
0x7a, 0x65, 0x22, 0x36, 0x0a, 0x12, 0x4c, 0x65, 0x61, 0x73, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65,
|
|
||||||
0x73, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x20, 0x0a, 0x0b, 0x43, 0x68, 0x6f, 0x69,
|
|
||||||
0x63, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0b, 0x43,
|
|
||||||
0x68, 0x6f, 0x69, 0x63, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0xd3, 0x01, 0x0a, 0x0a, 0x48,
|
|
||||||
0x61, 0x73, 0x68, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x46, 0x69, 0x65,
|
|
||||||
0x6c, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x12,
|
|
||||||
0x1e, 0x0a, 0x0a, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20,
|
|
||||||
0x01, 0x28, 0x09, 0x52, 0x0a, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12,
|
|
||||||
0x57, 0x0a, 0x0c, 0x43, 0x6f, 0x6f, 0x6b, 0x69, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18,
|
|
||||||
0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 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,
|
0x70, 0x2e, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61,
|
||||||
0x6c, 0x2e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x65, 0x6e, 0x74, 0x72, 0x79, 0x2e, 0x43, 0x6f,
|
0x6c, 0x2e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x65, 0x6e, 0x74, 0x72, 0x79, 0x2e, 0x53, 0x65,
|
||||||
0x6f, 0x6b, 0x69, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x0c, 0x43, 0x6f, 0x6f, 0x6b,
|
0x72, 0x76, 0x69, 0x63, 0x65, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x72, 0x46, 0x61, 0x69,
|
||||||
0x69, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x1a, 0x0a, 0x08, 0x53, 0x6f, 0x75, 0x72,
|
0x6c, 0x6f, 0x76, 0x65, 0x72, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x52, 0x07, 0x54, 0x61, 0x72,
|
||||||
0x63, 0x65, 0x49, 0x50, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x53, 0x6f, 0x75, 0x72,
|
0x67, 0x65, 0x74, 0x73, 0x22, 0xcf, 0x01, 0x0a, 0x1d, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65,
|
||||||
0x63, 0x65, 0x49, 0x50, 0x12, 0x1a, 0x0a, 0x08, 0x54, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x61, 0x6c,
|
0x52, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x72, 0x46, 0x61, 0x69, 0x6c, 0x6f, 0x76, 0x65, 0x72,
|
||||||
0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x54, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x61, 0x6c,
|
0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63,
|
||||||
0x22, 0x69, 0x0a, 0x0c, 0x43, 0x6f, 0x6f, 0x6b, 0x69, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67,
|
0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65,
|
||||||
0x12, 0x18, 0x0a, 0x07, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28,
|
0x12, 0x24, 0x0a, 0x0d, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x53, 0x75, 0x62, 0x73, 0x65,
|
||||||
0x08, 0x52, 0x07, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x2b, 0x0a, 0x03, 0x54, 0x54,
|
0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65,
|
||||||
0x4c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65,
|
0x53, 0x75, 0x62, 0x73, 0x65, 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x50, 0x61, 0x72, 0x74, 0x69, 0x74,
|
||||||
0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69,
|
0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x50, 0x61, 0x72, 0x74, 0x69,
|
||||||
0x6f, 0x6e, 0x52, 0x03, 0x54, 0x54, 0x4c, 0x12, 0x12, 0x0a, 0x04, 0x50, 0x61, 0x74, 0x68, 0x18,
|
0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1c, 0x0a, 0x09, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63,
|
||||||
0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x50, 0x61, 0x74, 0x68, 0x22, 0xbf, 0x02, 0x0a, 0x0e,
|
0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61,
|
||||||
0x49, 0x6e, 0x67, 0x72, 0x65, 0x73, 0x73, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x12, 0x49,
|
0x63, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x44, 0x61, 0x74, 0x61, 0x63, 0x65, 0x6e, 0x74, 0x65, 0x72,
|
||||||
0x0a, 0x03, 0x54, 0x4c, 0x53, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x37, 0x2e, 0x68, 0x61,
|
0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x44, 0x61, 0x74, 0x61, 0x63, 0x65, 0x6e, 0x74,
|
||||||
0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2e, 0x69,
|
0x65, 0x72, 0x12, 0x12, 0x0a, 0x04, 0x50, 0x65, 0x65, 0x72, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09,
|
||||||
0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x65, 0x6e,
|
0x52, 0x04, 0x50, 0x65, 0x65, 0x72, 0x22, 0xc7, 0x02, 0x0a, 0x0c, 0x4c, 0x6f, 0x61, 0x64, 0x42,
|
||||||
0x74, 0x72, 0x79, 0x2e, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x54, 0x4c, 0x53, 0x43, 0x6f,
|
0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x72, 0x12, 0x16, 0x0a, 0x06, 0x50, 0x6f, 0x6c, 0x69, 0x63,
|
||||||
0x6e, 0x66, 0x69, 0x67, 0x52, 0x03, 0x54, 0x4c, 0x53, 0x12, 0x54, 0x0a, 0x09, 0x4c, 0x69, 0x73,
|
0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x12,
|
||||||
0x74, 0x65, 0x6e, 0x65, 0x72, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x36, 0x2e, 0x68,
|
0x5d, 0x0a, 0x0e, 0x52, 0x69, 0x6e, 0x67, 0x48, 0x61, 0x73, 0x68, 0x43, 0x6f, 0x6e, 0x66, 0x69,
|
||||||
0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2e,
|
0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x35, 0x2e, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63,
|
||||||
0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x65,
|
0x6f, 0x72, 0x70, 0x2e, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72,
|
||||||
0x6e, 0x74, 0x72, 0x79, 0x2e, 0x49, 0x6e, 0x67, 0x72, 0x65, 0x73, 0x73, 0x4c, 0x69, 0x73, 0x74,
|
0x6e, 0x61, 0x6c, 0x2e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x65, 0x6e, 0x74, 0x72, 0x79, 0x2e,
|
||||||
0x65, 0x6e, 0x65, 0x72, 0x52, 0x09, 0x4c, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x65, 0x72, 0x73, 0x12,
|
0x52, 0x69, 0x6e, 0x67, 0x48, 0x61, 0x73, 0x68, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x0e,
|
||||||
0x53, 0x0a, 0x04, 0x4d, 0x65, 0x74, 0x61, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x3f, 0x2e,
|
0x52, 0x69, 0x6e, 0x67, 0x48, 0x61, 0x73, 0x68, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x69,
|
||||||
0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6c,
|
0x0a, 0x12, 0x4c, 0x65, 0x61, 0x73, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x43, 0x6f,
|
||||||
0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67,
|
0x6e, 0x66, 0x69, 0x67, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x39, 0x2e, 0x68, 0x61, 0x73,
|
||||||
0x65, 0x6e, 0x74, 0x72, 0x79, 0x2e, 0x49, 0x6e, 0x67, 0x72, 0x65, 0x73, 0x73, 0x47, 0x61, 0x74,
|
0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2e, 0x69, 0x6e,
|
||||||
0x65, 0x77, 0x61, 0x79, 0x2e, 0x4d, 0x65, 0x74, 0x61, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x04,
|
0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x65, 0x6e, 0x74,
|
||||||
0x4d, 0x65, 0x74, 0x61, 0x1a, 0x37, 0x0a, 0x09, 0x4d, 0x65, 0x74, 0x61, 0x45, 0x6e, 0x74, 0x72,
|
0x72, 0x79, 0x2e, 0x4c, 0x65, 0x61, 0x73, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x43,
|
||||||
0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03,
|
0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x12, 0x4c, 0x65, 0x61, 0x73, 0x74, 0x52, 0x65, 0x71, 0x75,
|
||||||
0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01,
|
0x65, 0x73, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x55, 0x0a, 0x0c, 0x48, 0x61, 0x73,
|
||||||
0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xea, 0x01,
|
0x68, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x69, 0x65, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32,
|
||||||
0x0a, 0x10, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x54, 0x4c, 0x53, 0x43, 0x6f, 0x6e, 0x66,
|
0x31, 0x2e, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x63, 0x6f, 0x6e, 0x73,
|
||||||
0x69, 0x67, 0x12, 0x18, 0x0a, 0x07, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x01, 0x20,
|
0x75, 0x6c, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2e, 0x63, 0x6f, 0x6e, 0x66,
|
||||||
0x01, 0x28, 0x08, 0x52, 0x07, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x4c, 0x0a, 0x03,
|
0x69, 0x67, 0x65, 0x6e, 0x74, 0x72, 0x79, 0x2e, 0x48, 0x61, 0x73, 0x68, 0x50, 0x6f, 0x6c, 0x69,
|
||||||
0x53, 0x44, 0x53, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x3a, 0x2e, 0x68, 0x61, 0x73, 0x68,
|
0x63, 0x79, 0x52, 0x0c, 0x48, 0x61, 0x73, 0x68, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x69, 0x65, 0x73,
|
||||||
|
0x22, 0x64, 0x0a, 0x0e, 0x52, 0x69, 0x6e, 0x67, 0x48, 0x61, 0x73, 0x68, 0x43, 0x6f, 0x6e, 0x66,
|
||||||
|
0x69, 0x67, 0x12, 0x28, 0x0a, 0x0f, 0x4d, 0x69, 0x6e, 0x69, 0x6d, 0x75, 0x6d, 0x52, 0x69, 0x6e,
|
||||||
|
0x67, 0x53, 0x69, 0x7a, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0f, 0x4d, 0x69, 0x6e,
|
||||||
|
0x69, 0x6d, 0x75, 0x6d, 0x52, 0x69, 0x6e, 0x67, 0x53, 0x69, 0x7a, 0x65, 0x12, 0x28, 0x0a, 0x0f,
|
||||||
|
0x4d, 0x61, 0x78, 0x69, 0x6d, 0x75, 0x6d, 0x52, 0x69, 0x6e, 0x67, 0x53, 0x69, 0x7a, 0x65, 0x18,
|
||||||
|
0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0f, 0x4d, 0x61, 0x78, 0x69, 0x6d, 0x75, 0x6d, 0x52, 0x69,
|
||||||
|
0x6e, 0x67, 0x53, 0x69, 0x7a, 0x65, 0x22, 0x36, 0x0a, 0x12, 0x4c, 0x65, 0x61, 0x73, 0x74, 0x52,
|
||||||
|
0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x20, 0x0a, 0x0b,
|
||||||
|
0x43, 0x68, 0x6f, 0x69, 0x63, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28,
|
||||||
|
0x0d, 0x52, 0x0b, 0x43, 0x68, 0x6f, 0x69, 0x63, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0xd3,
|
||||||
|
0x01, 0x0a, 0x0a, 0x48, 0x61, 0x73, 0x68, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x12, 0x14, 0x0a,
|
||||||
|
0x05, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x46, 0x69,
|
||||||
|
0x65, 0x6c, 0x64, 0x12, 0x1e, 0x0a, 0x0a, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x56, 0x61, 0x6c, 0x75,
|
||||||
|
0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x56, 0x61,
|
||||||
|
0x6c, 0x75, 0x65, 0x12, 0x57, 0x0a, 0x0c, 0x43, 0x6f, 0x6f, 0x6b, 0x69, 0x65, 0x43, 0x6f, 0x6e,
|
||||||
|
0x66, 0x69, 0x67, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x33, 0x2e, 0x68, 0x61, 0x73, 0x68,
|
||||||
0x69, 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2e, 0x69, 0x6e, 0x74,
|
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, 0x66, 0x69, 0x67, 0x65, 0x6e, 0x74, 0x72,
|
0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x65, 0x6e, 0x74, 0x72,
|
||||||
0x79, 0x2e, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x54, 0x4c, 0x53, 0x53, 0x44, 0x53, 0x43,
|
0x79, 0x2e, 0x43, 0x6f, 0x6f, 0x6b, 0x69, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x0c,
|
||||||
0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x03, 0x53, 0x44, 0x53, 0x12, 0x24, 0x0a, 0x0d, 0x54, 0x4c,
|
0x43, 0x6f, 0x6f, 0x6b, 0x69, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x1a, 0x0a, 0x08,
|
||||||
0x53, 0x4d, 0x69, 0x6e, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28,
|
0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x50, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08,
|
||||||
0x09, 0x52, 0x0d, 0x54, 0x4c, 0x53, 0x4d, 0x69, 0x6e, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e,
|
0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x50, 0x12, 0x1a, 0x0a, 0x08, 0x54, 0x65, 0x72, 0x6d,
|
||||||
0x12, 0x24, 0x0a, 0x0d, 0x54, 0x4c, 0x53, 0x4d, 0x61, 0x78, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f,
|
0x69, 0x6e, 0x61, 0x6c, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x54, 0x65, 0x72, 0x6d,
|
||||||
0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x54, 0x4c, 0x53, 0x4d, 0x61, 0x78, 0x56,
|
0x69, 0x6e, 0x61, 0x6c, 0x22, 0x69, 0x0a, 0x0c, 0x43, 0x6f, 0x6f, 0x6b, 0x69, 0x65, 0x43, 0x6f,
|
||||||
0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x22, 0x0a, 0x0c, 0x43, 0x69, 0x70, 0x68, 0x65, 0x72,
|
0x6e, 0x66, 0x69, 0x67, 0x12, 0x18, 0x0a, 0x07, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x18,
|
||||||
0x53, 0x75, 0x69, 0x74, 0x65, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0c, 0x43, 0x69,
|
0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x2b,
|
||||||
0x70, 0x68, 0x65, 0x72, 0x53, 0x75, 0x69, 0x74, 0x65, 0x73, 0x22, 0x5b, 0x0a, 0x13, 0x47, 0x61,
|
0x0a, 0x03, 0x54, 0x54, 0x4c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f,
|
||||||
0x74, 0x65, 0x77, 0x61, 0x79, 0x54, 0x4c, 0x53, 0x53, 0x44, 0x53, 0x43, 0x6f, 0x6e, 0x66, 0x69,
|
0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75,
|
||||||
0x67, 0x12, 0x20, 0x0a, 0x0b, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65,
|
0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x03, 0x54, 0x54, 0x4c, 0x12, 0x12, 0x0a, 0x04, 0x50,
|
||||||
0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x4e,
|
0x61, 0x74, 0x68, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x50, 0x61, 0x74, 0x68, 0x22,
|
||||||
0x61, 0x6d, 0x65, 0x12, 0x22, 0x0a, 0x0c, 0x43, 0x65, 0x72, 0x74, 0x52, 0x65, 0x73, 0x6f, 0x75,
|
0xbf, 0x02, 0x0a, 0x0e, 0x49, 0x6e, 0x67, 0x72, 0x65, 0x73, 0x73, 0x47, 0x61, 0x74, 0x65, 0x77,
|
||||||
0x72, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x43, 0x65, 0x72, 0x74, 0x52,
|
0x61, 0x79, 0x12, 0x49, 0x0a, 0x03, 0x54, 0x4c, 0x53, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32,
|
||||||
0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x22, 0xdf, 0x01, 0x0a, 0x0f, 0x49, 0x6e, 0x67, 0x72,
|
0x37, 0x2e, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x63, 0x6f, 0x6e, 0x73,
|
||||||
0x65, 0x73, 0x73, 0x4c, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x65, 0x72, 0x12, 0x12, 0x0a, 0x04, 0x50,
|
0x75, 0x6c, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2e, 0x63, 0x6f, 0x6e, 0x66,
|
||||||
0x6f, 0x72, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x04, 0x50, 0x6f, 0x72, 0x74, 0x12,
|
0x69, 0x67, 0x65, 0x6e, 0x74, 0x72, 0x79, 0x2e, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x54,
|
||||||
0x1a, 0x0a, 0x08, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28,
|
0x4c, 0x53, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x03, 0x54, 0x4c, 0x53, 0x12, 0x54, 0x0a,
|
||||||
0x09, 0x52, 0x08, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x12, 0x51, 0x0a, 0x08, 0x53,
|
0x09, 0x4c, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x65, 0x72, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b,
|
||||||
0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x35, 0x2e,
|
0x32, 0x36, 0x2e, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x63, 0x6f, 0x6e,
|
||||||
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, 0x66, 0x69, 0x67,
|
|
||||||
0x65, 0x6e, 0x74, 0x72, 0x79, 0x2e, 0x49, 0x6e, 0x67, 0x72, 0x65, 0x73, 0x73, 0x53, 0x65, 0x72,
|
|
||||||
0x76, 0x69, 0x63, 0x65, 0x52, 0x08, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x12, 0x49,
|
|
||||||
0x0a, 0x03, 0x54, 0x4c, 0x53, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 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, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x65, 0x6e,
|
|
||||||
0x74, 0x72, 0x79, 0x2e, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x54, 0x4c, 0x53, 0x43, 0x6f,
|
|
||||||
0x6e, 0x66, 0x69, 0x67, 0x52, 0x03, 0x54, 0x4c, 0x53, 0x22, 0xbe, 0x04, 0x0a, 0x0e, 0x49, 0x6e,
|
|
||||||
0x67, 0x72, 0x65, 0x73, 0x73, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x12, 0x0a, 0x04,
|
|
||||||
0x4e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x4e, 0x61, 0x6d, 0x65,
|
|
||||||
0x12, 0x14, 0x0a, 0x05, 0x48, 0x6f, 0x73, 0x74, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52,
|
|
||||||
0x05, 0x48, 0x6f, 0x73, 0x74, 0x73, 0x12, 0x50, 0x0a, 0x03, 0x54, 0x4c, 0x53, 0x18, 0x03, 0x20,
|
|
||||||
0x01, 0x28, 0x0b, 0x32, 0x3e, 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, 0x66, 0x69, 0x67, 0x65, 0x6e, 0x74, 0x72, 0x79, 0x2e, 0x47, 0x61, 0x74, 0x65,
|
|
||||||
0x77, 0x61, 0x79, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x54, 0x4c, 0x53, 0x43, 0x6f, 0x6e,
|
|
||||||
0x66, 0x69, 0x67, 0x52, 0x03, 0x54, 0x4c, 0x53, 0x12, 0x62, 0x0a, 0x0e, 0x52, 0x65, 0x71, 0x75,
|
|
||||||
0x65, 0x73, 0x74, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b,
|
|
||||||
0x32, 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, 0x63, 0x6f, 0x6e,
|
|
||||||
0x66, 0x69, 0x67, 0x65, 0x6e, 0x74, 0x72, 0x79, 0x2e, 0x48, 0x54, 0x54, 0x50, 0x48, 0x65, 0x61,
|
|
||||||
0x64, 0x65, 0x72, 0x4d, 0x6f, 0x64, 0x69, 0x66, 0x69, 0x65, 0x72, 0x73, 0x52, 0x0e, 0x52, 0x65,
|
|
||||||
0x71, 0x75, 0x65, 0x73, 0x74, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x12, 0x64, 0x0a, 0x0f,
|
|
||||||
0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x18,
|
|
||||||
0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 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, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x65, 0x6e, 0x74, 0x72, 0x79, 0x2e, 0x48, 0x54,
|
|
||||||
0x54, 0x50, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x4d, 0x6f, 0x64, 0x69, 0x66, 0x69, 0x65, 0x72,
|
|
||||||
0x73, 0x52, 0x0f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x48, 0x65, 0x61, 0x64, 0x65,
|
|
||||||
0x72, 0x73, 0x12, 0x53, 0x0a, 0x04, 0x4d, 0x65, 0x74, 0x61, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b,
|
|
||||||
0x32, 0x3f, 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,
|
0x73, 0x75, 0x6c, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2e, 0x63, 0x6f, 0x6e,
|
||||||
0x66, 0x69, 0x67, 0x65, 0x6e, 0x74, 0x72, 0x79, 0x2e, 0x49, 0x6e, 0x67, 0x72, 0x65, 0x73, 0x73,
|
0x66, 0x69, 0x67, 0x65, 0x6e, 0x74, 0x72, 0x79, 0x2e, 0x49, 0x6e, 0x67, 0x72, 0x65, 0x73, 0x73,
|
||||||
0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x4d, 0x65, 0x74, 0x61, 0x45, 0x6e, 0x74, 0x72,
|
0x4c, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x65, 0x72, 0x52, 0x09, 0x4c, 0x69, 0x73, 0x74, 0x65, 0x6e,
|
||||||
0x79, 0x52, 0x04, 0x4d, 0x65, 0x74, 0x61, 0x12, 0x58, 0x0a, 0x0e, 0x45, 0x6e, 0x74, 0x65, 0x72,
|
0x65, 0x72, 0x73, 0x12, 0x53, 0x0a, 0x04, 0x4d, 0x65, 0x74, 0x61, 0x18, 0x03, 0x20, 0x03, 0x28,
|
||||||
0x70, 0x72, 0x69, 0x73, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32,
|
0x0b, 0x32, 0x3f, 0x2e, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x63, 0x6f,
|
||||||
0x30, 0x2e, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x63, 0x6f, 0x6e, 0x73,
|
0x6e, 0x73, 0x75, 0x6c, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2e, 0x63, 0x6f,
|
||||||
0x75, 0x6c, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2e, 0x63, 0x6f, 0x6d, 0x6d,
|
0x6e, 0x66, 0x69, 0x67, 0x65, 0x6e, 0x74, 0x72, 0x79, 0x2e, 0x49, 0x6e, 0x67, 0x72, 0x65, 0x73,
|
||||||
0x6f, 0x6e, 0x2e, 0x45, 0x6e, 0x74, 0x65, 0x72, 0x70, 0x72, 0x69, 0x73, 0x65, 0x4d, 0x65, 0x74,
|
0x73, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x2e, 0x4d, 0x65, 0x74, 0x61, 0x45, 0x6e, 0x74,
|
||||||
0x61, 0x52, 0x0e, 0x45, 0x6e, 0x74, 0x65, 0x72, 0x70, 0x72, 0x69, 0x73, 0x65, 0x4d, 0x65, 0x74,
|
0x72, 0x79, 0x52, 0x04, 0x4d, 0x65, 0x74, 0x61, 0x1a, 0x37, 0x0a, 0x09, 0x4d, 0x65, 0x74, 0x61,
|
||||||
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, 0x67, 0x0a, 0x17, 0x47, 0x61,
|
|
||||||
0x74, 0x65, 0x77, 0x61, 0x79, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x54, 0x4c, 0x53, 0x43,
|
|
||||||
0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x4c, 0x0a, 0x03, 0x53, 0x44, 0x53, 0x18, 0x01, 0x20, 0x01,
|
|
||||||
0x28, 0x0b, 0x32, 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, 0x63,
|
|
||||||
0x6f, 0x6e, 0x66, 0x69, 0x67, 0x65, 0x6e, 0x74, 0x72, 0x79, 0x2e, 0x47, 0x61, 0x74, 0x65, 0x77,
|
|
||||||
0x61, 0x79, 0x54, 0x4c, 0x53, 0x53, 0x44, 0x53, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x03,
|
|
||||||
0x53, 0x44, 0x53, 0x22, 0xcb, 0x02, 0x0a, 0x13, 0x48, 0x54, 0x54, 0x50, 0x48, 0x65, 0x61, 0x64,
|
|
||||||
0x65, 0x72, 0x4d, 0x6f, 0x64, 0x69, 0x66, 0x69, 0x65, 0x72, 0x73, 0x12, 0x55, 0x0a, 0x03, 0x41,
|
|
||||||
0x64, 0x64, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 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, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x65, 0x6e, 0x74, 0x72, 0x79,
|
|
||||||
0x2e, 0x48, 0x54, 0x54, 0x50, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x4d, 0x6f, 0x64, 0x69, 0x66,
|
|
||||||
0x69, 0x65, 0x72, 0x73, 0x2e, 0x41, 0x64, 0x64, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x03, 0x41,
|
|
||||||
0x64, 0x64, 0x12, 0x55, 0x0a, 0x03, 0x53, 0x65, 0x74, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32,
|
|
||||||
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, 0x63, 0x6f, 0x6e, 0x66,
|
|
||||||
0x69, 0x67, 0x65, 0x6e, 0x74, 0x72, 0x79, 0x2e, 0x48, 0x54, 0x54, 0x50, 0x48, 0x65, 0x61, 0x64,
|
|
||||||
0x65, 0x72, 0x4d, 0x6f, 0x64, 0x69, 0x66, 0x69, 0x65, 0x72, 0x73, 0x2e, 0x53, 0x65, 0x74, 0x45,
|
|
||||||
0x6e, 0x74, 0x72, 0x79, 0x52, 0x03, 0x53, 0x65, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x52, 0x65, 0x6d,
|
|
||||||
0x6f, 0x76, 0x65, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x06, 0x52, 0x65, 0x6d, 0x6f, 0x76,
|
|
||||||
0x65, 0x1a, 0x36, 0x0a, 0x08, 0x41, 0x64, 0x64, 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, 0x1a, 0x36, 0x0a, 0x08, 0x53, 0x65, 0x74,
|
|
||||||
0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01,
|
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,
|
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,
|
0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38,
|
||||||
0x01, 0x22, 0xf6, 0x01, 0x0a, 0x11, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x49, 0x6e, 0x74,
|
0x01, 0x22, 0xea, 0x01, 0x0a, 0x10, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x54, 0x4c, 0x53,
|
||||||
0x65, 0x6e, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x50, 0x0a, 0x07, 0x53, 0x6f, 0x75, 0x72, 0x63,
|
0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x18, 0x0a, 0x07, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65,
|
||||||
0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x36, 0x2e, 0x68, 0x61, 0x73, 0x68, 0x69,
|
0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64,
|
||||||
0x63, 0x6f, 0x72, 0x70, 0x2e, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2e, 0x69, 0x6e, 0x74, 0x65,
|
0x12, 0x4c, 0x0a, 0x03, 0x53, 0x44, 0x53, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x3a, 0x2e,
|
||||||
0x72, 0x6e, 0x61, 0x6c, 0x2e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x65, 0x6e, 0x74, 0x72, 0x79,
|
|
||||||
0x2e, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x69, 0x6f, 0x6e,
|
|
||||||
0x52, 0x07, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x12, 0x56, 0x0a, 0x04, 0x4d, 0x65, 0x74,
|
|
||||||
0x61, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 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, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x65, 0x6e, 0x74, 0x72, 0x79, 0x2e,
|
|
||||||
0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x49, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x69, 0x6f, 0x6e,
|
|
||||||
0x73, 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, 0xa6, 0x06, 0x0a, 0x0f, 0x53,
|
|
||||||
0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x12,
|
|
||||||
0x0a, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x4e, 0x61,
|
|
||||||
0x6d, 0x65, 0x12, 0x4e, 0x0a, 0x06, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01,
|
|
||||||
0x28, 0x0e, 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, 0x63,
|
|
||||||
0x6f, 0x6e, 0x66, 0x69, 0x67, 0x65, 0x6e, 0x74, 0x72, 0x79, 0x2e, 0x49, 0x6e, 0x74, 0x65, 0x6e,
|
|
||||||
0x74, 0x69, 0x6f, 0x6e, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x06, 0x41, 0x63, 0x74, 0x69,
|
|
||||||
0x6f, 0x6e, 0x12, 0x5c, 0x0a, 0x0b, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e,
|
|
||||||
0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 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, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x65, 0x6e, 0x74, 0x72, 0x79, 0x2e,
|
|
||||||
0x49, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73,
|
|
||||||
0x69, 0x6f, 0x6e, 0x52, 0x0b, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73,
|
|
||||||
0x12, 0x1e, 0x0a, 0x0a, 0x50, 0x72, 0x65, 0x63, 0x65, 0x64, 0x65, 0x6e, 0x63, 0x65, 0x18, 0x04,
|
|
||||||
0x20, 0x01, 0x28, 0x05, 0x52, 0x0a, 0x50, 0x72, 0x65, 0x63, 0x65, 0x64, 0x65, 0x6e, 0x63, 0x65,
|
|
||||||
0x12, 0x1a, 0x0a, 0x08, 0x4c, 0x65, 0x67, 0x61, 0x63, 0x79, 0x49, 0x44, 0x18, 0x05, 0x20, 0x01,
|
|
||||||
0x28, 0x09, 0x52, 0x08, 0x4c, 0x65, 0x67, 0x61, 0x63, 0x79, 0x49, 0x44, 0x12, 0x4e, 0x0a, 0x04,
|
|
||||||
0x54, 0x79, 0x70, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0e, 0x32, 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, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x65, 0x6e, 0x74,
|
|
||||||
0x72, 0x79, 0x2e, 0x49, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x6f, 0x75, 0x72,
|
|
||||||
0x63, 0x65, 0x54, 0x79, 0x70, 0x65, 0x52, 0x04, 0x54, 0x79, 0x70, 0x65, 0x12, 0x20, 0x0a, 0x0b,
|
|
||||||
0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x07, 0x20, 0x01, 0x28,
|
|
||||||
0x09, 0x52, 0x0b, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x66,
|
|
||||||
0x0a, 0x0a, 0x4c, 0x65, 0x67, 0x61, 0x63, 0x79, 0x4d, 0x65, 0x74, 0x61, 0x18, 0x08, 0x20, 0x03,
|
|
||||||
0x28, 0x0b, 0x32, 0x46, 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, 0x66, 0x69, 0x67, 0x65, 0x6e, 0x74, 0x72, 0x79, 0x2e, 0x53, 0x6f, 0x75, 0x72, 0x63,
|
|
||||||
0x65, 0x49, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x4c, 0x65, 0x67, 0x61, 0x63,
|
|
||||||
0x79, 0x4d, 0x65, 0x74, 0x61, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0a, 0x4c, 0x65, 0x67, 0x61,
|
|
||||||
0x63, 0x79, 0x4d, 0x65, 0x74, 0x61, 0x12, 0x46, 0x0a, 0x10, 0x4c, 0x65, 0x67, 0x61, 0x63, 0x79,
|
|
||||||
0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, 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, 0x10, 0x4c, 0x65,
|
|
||||||
0x67, 0x61, 0x63, 0x79, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x46,
|
|
||||||
0x0a, 0x10, 0x4c, 0x65, 0x67, 0x61, 0x63, 0x79, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x54, 0x69,
|
|
||||||
0x6d, 0x65, 0x18, 0x0a, 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, 0x10, 0x4c, 0x65, 0x67, 0x61, 0x63, 0x79, 0x55, 0x70, 0x64, 0x61,
|
|
||||||
0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x58, 0x0a, 0x0e, 0x45, 0x6e, 0x74, 0x65, 0x72, 0x70,
|
|
||||||
0x72, 0x69, 0x73, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x18, 0x0b, 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, 0x12, 0x0a, 0x04, 0x50, 0x65, 0x65, 0x72, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04,
|
|
||||||
0x50, 0x65, 0x65, 0x72, 0x1a, 0x3d, 0x0a, 0x0f, 0x4c, 0x65, 0x67, 0x61, 0x63, 0x79, 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, 0xb9, 0x01, 0x0a, 0x13, 0x49, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x69, 0x6f,
|
|
||||||
0x6e, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x4e, 0x0a, 0x06, 0x41,
|
|
||||||
0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 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, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x65, 0x6e,
|
|
||||||
0x74, 0x72, 0x79, 0x2e, 0x49, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x69, 0x6f, 0x6e, 0x41, 0x63, 0x74,
|
|
||||||
0x69, 0x6f, 0x6e, 0x52, 0x06, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x52, 0x0a, 0x04, 0x48,
|
|
||||||
0x54, 0x54, 0x50, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x3e, 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, 0x66, 0x69, 0x67, 0x65, 0x6e, 0x74, 0x72,
|
|
||||||
0x79, 0x2e, 0x49, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x69, 0x6f, 0x6e, 0x48, 0x54, 0x54, 0x50, 0x50,
|
|
||||||
0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x04, 0x48, 0x54, 0x54, 0x50, 0x22,
|
|
||||||
0xed, 0x01, 0x0a, 0x17, 0x49, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x69, 0x6f, 0x6e, 0x48, 0x54, 0x54,
|
|
||||||
0x50, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x1c, 0x0a, 0x09, 0x50,
|
|
||||||
0x61, 0x74, 0x68, 0x45, 0x78, 0x61, 0x63, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09,
|
|
||||||
0x50, 0x61, 0x74, 0x68, 0x45, 0x78, 0x61, 0x63, 0x74, 0x12, 0x1e, 0x0a, 0x0a, 0x50, 0x61, 0x74,
|
|
||||||
0x68, 0x50, 0x72, 0x65, 0x66, 0x69, 0x78, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x50,
|
|
||||||
0x61, 0x74, 0x68, 0x50, 0x72, 0x65, 0x66, 0x69, 0x78, 0x12, 0x1c, 0x0a, 0x09, 0x50, 0x61, 0x74,
|
|
||||||
0x68, 0x52, 0x65, 0x67, 0x65, 0x78, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x50, 0x61,
|
|
||||||
0x74, 0x68, 0x52, 0x65, 0x67, 0x65, 0x78, 0x12, 0x5c, 0x0a, 0x06, 0x48, 0x65, 0x61, 0x64, 0x65,
|
|
||||||
0x72, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x44, 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, 0x66, 0x69, 0x67, 0x65, 0x6e, 0x74, 0x72, 0x79, 0x2e,
|
|
||||||
0x49, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x69, 0x6f, 0x6e, 0x48, 0x54, 0x54, 0x50, 0x48, 0x65, 0x61,
|
|
||||||
0x64, 0x65, 0x72, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x06, 0x48,
|
|
||||||
0x65, 0x61, 0x64, 0x65, 0x72, 0x12, 0x18, 0x0a, 0x07, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x73,
|
|
||||||
0x18, 0x05, 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x73, 0x22,
|
|
||||||
0xc1, 0x01, 0x0a, 0x1d, 0x49, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x69, 0x6f, 0x6e, 0x48, 0x54, 0x54,
|
|
||||||
0x50, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f,
|
|
||||||
0x6e, 0x12, 0x12, 0x0a, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52,
|
|
||||||
0x04, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x50, 0x72, 0x65, 0x73, 0x65, 0x6e, 0x74,
|
|
||||||
0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x50, 0x72, 0x65, 0x73, 0x65, 0x6e, 0x74, 0x12,
|
|
||||||
0x14, 0x0a, 0x05, 0x45, 0x78, 0x61, 0x63, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05,
|
|
||||||
0x45, 0x78, 0x61, 0x63, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x50, 0x72, 0x65, 0x66, 0x69, 0x78, 0x18,
|
|
||||||
0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x50, 0x72, 0x65, 0x66, 0x69, 0x78, 0x12, 0x16, 0x0a,
|
|
||||||
0x06, 0x53, 0x75, 0x66, 0x66, 0x69, 0x78, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x53,
|
|
||||||
0x75, 0x66, 0x66, 0x69, 0x78, 0x12, 0x14, 0x0a, 0x05, 0x52, 0x65, 0x67, 0x65, 0x78, 0x18, 0x06,
|
|
||||||
0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x52, 0x65, 0x67, 0x65, 0x78, 0x12, 0x16, 0x0a, 0x06, 0x49,
|
|
||||||
0x6e, 0x76, 0x65, 0x72, 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x49, 0x6e, 0x76,
|
|
||||||
0x65, 0x72, 0x74, 0x2a, 0x77, 0x0a, 0x04, 0x4b, 0x69, 0x6e, 0x64, 0x12, 0x0f, 0x0a, 0x0b, 0x4b,
|
|
||||||
0x69, 0x6e, 0x64, 0x55, 0x6e, 0x6b, 0x6e, 0x6f, 0x77, 0x6e, 0x10, 0x00, 0x12, 0x12, 0x0a, 0x0e,
|
|
||||||
0x4b, 0x69, 0x6e, 0x64, 0x4d, 0x65, 0x73, 0x68, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x10, 0x01,
|
|
||||||
0x12, 0x17, 0x0a, 0x13, 0x4b, 0x69, 0x6e, 0x64, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x52,
|
|
||||||
0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x72, 0x10, 0x02, 0x12, 0x16, 0x0a, 0x12, 0x4b, 0x69, 0x6e,
|
|
||||||
0x64, 0x49, 0x6e, 0x67, 0x72, 0x65, 0x73, 0x73, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x10,
|
|
||||||
0x03, 0x12, 0x19, 0x0a, 0x15, 0x4b, 0x69, 0x6e, 0x64, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65,
|
|
||||||
0x49, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x10, 0x04, 0x2a, 0x26, 0x0a, 0x0f,
|
|
||||||
0x49, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x69, 0x6f, 0x6e, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12,
|
|
||||||
0x08, 0x0a, 0x04, 0x44, 0x65, 0x6e, 0x79, 0x10, 0x00, 0x12, 0x09, 0x0a, 0x05, 0x41, 0x6c, 0x6c,
|
|
||||||
0x6f, 0x77, 0x10, 0x01, 0x2a, 0x21, 0x0a, 0x13, 0x49, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x69, 0x6f,
|
|
||||||
0x6e, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x54, 0x79, 0x70, 0x65, 0x12, 0x0a, 0x0a, 0x06, 0x43,
|
|
||||||
0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x10, 0x00, 0x42, 0xa6, 0x02, 0x0a, 0x29, 0x63, 0x6f, 0x6d, 0x2e,
|
|
||||||
0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6c,
|
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, 0x66, 0x69, 0x67,
|
0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67,
|
||||||
0x65, 0x6e, 0x74, 0x72, 0x79, 0x42, 0x10, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x45, 0x6e, 0x74,
|
0x65, 0x6e, 0x74, 0x72, 0x79, 0x2e, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x54, 0x4c, 0x53,
|
||||||
0x72, 0x79, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x2f, 0x67, 0x69, 0x74, 0x68, 0x75,
|
0x53, 0x44, 0x53, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x03, 0x53, 0x44, 0x53, 0x12, 0x24,
|
||||||
0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2f,
|
0x0a, 0x0d, 0x54, 0x4c, 0x53, 0x4d, 0x69, 0x6e, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18,
|
||||||
0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x70, 0x62, 0x63,
|
0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x54, 0x4c, 0x53, 0x4d, 0x69, 0x6e, 0x56, 0x65, 0x72,
|
||||||
0x6f, 0x6e, 0x66, 0x69, 0x67, 0x65, 0x6e, 0x74, 0x72, 0x79, 0xa2, 0x02, 0x04, 0x48, 0x43, 0x49,
|
0x73, 0x69, 0x6f, 0x6e, 0x12, 0x24, 0x0a, 0x0d, 0x54, 0x4c, 0x53, 0x4d, 0x61, 0x78, 0x56, 0x65,
|
||||||
0x43, 0xaa, 0x02, 0x25, 0x48, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x43, 0x6f,
|
0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x54, 0x4c, 0x53,
|
||||||
0x6e, 0x73, 0x75, 0x6c, 0x2e, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2e, 0x43, 0x6f,
|
0x4d, 0x61, 0x78, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x22, 0x0a, 0x0c, 0x43, 0x69,
|
||||||
0x6e, 0x66, 0x69, 0x67, 0x65, 0x6e, 0x74, 0x72, 0x79, 0xca, 0x02, 0x25, 0x48, 0x61, 0x73, 0x68,
|
0x70, 0x68, 0x65, 0x72, 0x53, 0x75, 0x69, 0x74, 0x65, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x09,
|
||||||
0x69, 0x63, 0x6f, 0x72, 0x70, 0x5c, 0x43, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x5c, 0x49, 0x6e, 0x74,
|
0x52, 0x0c, 0x43, 0x69, 0x70, 0x68, 0x65, 0x72, 0x53, 0x75, 0x69, 0x74, 0x65, 0x73, 0x22, 0x5b,
|
||||||
0x65, 0x72, 0x6e, 0x61, 0x6c, 0x5c, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x65, 0x6e, 0x74, 0x72,
|
0x0a, 0x13, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x54, 0x4c, 0x53, 0x53, 0x44, 0x53, 0x43,
|
||||||
0x79, 0xe2, 0x02, 0x31, 0x48, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x5c, 0x43, 0x6f,
|
0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x20, 0x0a, 0x0b, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72,
|
||||||
0x6e, 0x73, 0x75, 0x6c, 0x5c, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x5c, 0x43, 0x6f,
|
0x4e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x43, 0x6c, 0x75, 0x73,
|
||||||
0x6e, 0x66, 0x69, 0x67, 0x65, 0x6e, 0x74, 0x72, 0x79, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74,
|
0x74, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x22, 0x0a, 0x0c, 0x43, 0x65, 0x72, 0x74, 0x52,
|
||||||
0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x28, 0x48, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72,
|
0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x43,
|
||||||
0x70, 0x3a, 0x3a, 0x43, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x3a, 0x3a, 0x49, 0x6e, 0x74, 0x65, 0x72,
|
0x65, 0x72, 0x74, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x22, 0xdf, 0x01, 0x0a, 0x0f,
|
||||||
0x6e, 0x61, 0x6c, 0x3a, 0x3a, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x65, 0x6e, 0x74, 0x72, 0x79,
|
0x49, 0x6e, 0x67, 0x72, 0x65, 0x73, 0x73, 0x4c, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x65, 0x72, 0x12,
|
||||||
0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
0x12, 0x0a, 0x04, 0x50, 0x6f, 0x72, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x04, 0x50,
|
||||||
|
0x6f, 0x72, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x18,
|
||||||
|
0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x12,
|
||||||
|
0x51, 0x0a, 0x08, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x18, 0x03, 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, 0x63, 0x6f,
|
||||||
|
0x6e, 0x66, 0x69, 0x67, 0x65, 0x6e, 0x74, 0x72, 0x79, 0x2e, 0x49, 0x6e, 0x67, 0x72, 0x65, 0x73,
|
||||||
|
0x73, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x52, 0x08, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63,
|
||||||
|
0x65, 0x73, 0x12, 0x49, 0x0a, 0x03, 0x54, 0x4c, 0x53, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32,
|
||||||
|
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, 0x63, 0x6f, 0x6e, 0x66,
|
||||||
|
0x69, 0x67, 0x65, 0x6e, 0x74, 0x72, 0x79, 0x2e, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x54,
|
||||||
|
0x4c, 0x53, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x03, 0x54, 0x4c, 0x53, 0x22, 0xbe, 0x04,
|
||||||
|
0x0a, 0x0e, 0x49, 0x6e, 0x67, 0x72, 0x65, 0x73, 0x73, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65,
|
||||||
|
0x12, 0x12, 0x0a, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04,
|
||||||
|
0x4e, 0x61, 0x6d, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x48, 0x6f, 0x73, 0x74, 0x73, 0x18, 0x02, 0x20,
|
||||||
|
0x03, 0x28, 0x09, 0x52, 0x05, 0x48, 0x6f, 0x73, 0x74, 0x73, 0x12, 0x50, 0x0a, 0x03, 0x54, 0x4c,
|
||||||
|
0x53, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x3e, 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, 0x66, 0x69, 0x67, 0x65, 0x6e, 0x74, 0x72, 0x79, 0x2e,
|
||||||
|
0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x54, 0x4c,
|
||||||
|
0x53, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x03, 0x54, 0x4c, 0x53, 0x12, 0x62, 0x0a, 0x0e,
|
||||||
|
0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x18, 0x04,
|
||||||
|
0x20, 0x01, 0x28, 0x0b, 0x32, 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, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x65, 0x6e, 0x74, 0x72, 0x79, 0x2e, 0x48, 0x54, 0x54,
|
||||||
|
0x50, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x4d, 0x6f, 0x64, 0x69, 0x66, 0x69, 0x65, 0x72, 0x73,
|
||||||
|
0x52, 0x0e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73,
|
||||||
|
0x12, 0x64, 0x0a, 0x0f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x48, 0x65, 0x61, 0x64,
|
||||||
|
0x65, 0x72, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 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, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x65, 0x6e, 0x74, 0x72,
|
||||||
|
0x79, 0x2e, 0x48, 0x54, 0x54, 0x50, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x4d, 0x6f, 0x64, 0x69,
|
||||||
|
0x66, 0x69, 0x65, 0x72, 0x73, 0x52, 0x0f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x48,
|
||||||
|
0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x12, 0x53, 0x0a, 0x04, 0x4d, 0x65, 0x74, 0x61, 0x18, 0x06,
|
||||||
|
0x20, 0x03, 0x28, 0x0b, 0x32, 0x3f, 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, 0x66, 0x69, 0x67, 0x65, 0x6e, 0x74, 0x72, 0x79, 0x2e, 0x49, 0x6e, 0x67,
|
||||||
|
0x72, 0x65, 0x73, 0x73, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x4d, 0x65, 0x74, 0x61,
|
||||||
|
0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x04, 0x4d, 0x65, 0x74, 0x61, 0x12, 0x58, 0x0a, 0x0e, 0x45,
|
||||||
|
0x6e, 0x74, 0x65, 0x72, 0x70, 0x72, 0x69, 0x73, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x18, 0x07, 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, 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, 0x67,
|
||||||
|
0x0a, 0x17, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65,
|
||||||
|
0x54, 0x4c, 0x53, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x4c, 0x0a, 0x03, 0x53, 0x44, 0x53,
|
||||||
|
0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 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, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x65, 0x6e, 0x74, 0x72, 0x79, 0x2e, 0x47,
|
||||||
|
0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x54, 0x4c, 0x53, 0x53, 0x44, 0x53, 0x43, 0x6f, 0x6e, 0x66,
|
||||||
|
0x69, 0x67, 0x52, 0x03, 0x53, 0x44, 0x53, 0x22, 0xcb, 0x02, 0x0a, 0x13, 0x48, 0x54, 0x54, 0x50,
|
||||||
|
0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x4d, 0x6f, 0x64, 0x69, 0x66, 0x69, 0x65, 0x72, 0x73, 0x12,
|
||||||
|
0x55, 0x0a, 0x03, 0x41, 0x64, 0x64, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 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, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x65,
|
||||||
|
0x6e, 0x74, 0x72, 0x79, 0x2e, 0x48, 0x54, 0x54, 0x50, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x4d,
|
||||||
|
0x6f, 0x64, 0x69, 0x66, 0x69, 0x65, 0x72, 0x73, 0x2e, 0x41, 0x64, 0x64, 0x45, 0x6e, 0x74, 0x72,
|
||||||
|
0x79, 0x52, 0x03, 0x41, 0x64, 0x64, 0x12, 0x55, 0x0a, 0x03, 0x53, 0x65, 0x74, 0x18, 0x02, 0x20,
|
||||||
|
0x03, 0x28, 0x0b, 0x32, 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,
|
||||||
|
0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x65, 0x6e, 0x74, 0x72, 0x79, 0x2e, 0x48, 0x54, 0x54, 0x50,
|
||||||
|
0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x4d, 0x6f, 0x64, 0x69, 0x66, 0x69, 0x65, 0x72, 0x73, 0x2e,
|
||||||
|
0x53, 0x65, 0x74, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x03, 0x53, 0x65, 0x74, 0x12, 0x16, 0x0a,
|
||||||
|
0x06, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x06, 0x52,
|
||||||
|
0x65, 0x6d, 0x6f, 0x76, 0x65, 0x1a, 0x36, 0x0a, 0x08, 0x41, 0x64, 0x64, 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, 0x1a, 0x36, 0x0a,
|
||||||
|
0x08, 0x53, 0x65, 0x74, 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, 0xf6, 0x01, 0x0a, 0x11, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63,
|
||||||
|
0x65, 0x49, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x50, 0x0a, 0x07, 0x53,
|
||||||
|
0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 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, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x65,
|
||||||
|
0x6e, 0x74, 0x72, 0x79, 0x2e, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x6e, 0x74, 0x65, 0x6e,
|
||||||
|
0x74, 0x69, 0x6f, 0x6e, 0x52, 0x07, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x12, 0x56, 0x0a,
|
||||||
|
0x04, 0x4d, 0x65, 0x74, 0x61, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 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, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x65, 0x6e,
|
||||||
|
0x74, 0x72, 0x79, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x49, 0x6e, 0x74, 0x65, 0x6e,
|
||||||
|
0x74, 0x69, 0x6f, 0x6e, 0x73, 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, 0xa6,
|
||||||
|
0x06, 0x0a, 0x0f, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x69,
|
||||||
|
0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09,
|
||||||
|
0x52, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x4e, 0x0a, 0x06, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e,
|
||||||
|
0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 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, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x65, 0x6e, 0x74, 0x72, 0x79, 0x2e, 0x49,
|
||||||
|
0x6e, 0x74, 0x65, 0x6e, 0x74, 0x69, 0x6f, 0x6e, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x06,
|
||||||
|
0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x5c, 0x0a, 0x0b, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73,
|
||||||
|
0x73, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 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, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x65, 0x6e,
|
||||||
|
0x74, 0x72, 0x79, 0x2e, 0x49, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x65, 0x72,
|
||||||
|
0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x0b, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73,
|
||||||
|
0x69, 0x6f, 0x6e, 0x73, 0x12, 0x1e, 0x0a, 0x0a, 0x50, 0x72, 0x65, 0x63, 0x65, 0x64, 0x65, 0x6e,
|
||||||
|
0x63, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0a, 0x50, 0x72, 0x65, 0x63, 0x65, 0x64,
|
||||||
|
0x65, 0x6e, 0x63, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x4c, 0x65, 0x67, 0x61, 0x63, 0x79, 0x49, 0x44,
|
||||||
|
0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x4c, 0x65, 0x67, 0x61, 0x63, 0x79, 0x49, 0x44,
|
||||||
|
0x12, 0x4e, 0x0a, 0x04, 0x54, 0x79, 0x70, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0e, 0x32, 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, 0x63, 0x6f, 0x6e, 0x66, 0x69,
|
||||||
|
0x67, 0x65, 0x6e, 0x74, 0x72, 0x79, 0x2e, 0x49, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x69, 0x6f, 0x6e,
|
||||||
|
0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x54, 0x79, 0x70, 0x65, 0x52, 0x04, 0x54, 0x79, 0x70, 0x65,
|
||||||
|
0x12, 0x20, 0x0a, 0x0b, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18,
|
||||||
|
0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69,
|
||||||
|
0x6f, 0x6e, 0x12, 0x66, 0x0a, 0x0a, 0x4c, 0x65, 0x67, 0x61, 0x63, 0x79, 0x4d, 0x65, 0x74, 0x61,
|
||||||
|
0x18, 0x08, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x46, 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, 0x66, 0x69, 0x67, 0x65, 0x6e, 0x74, 0x72, 0x79, 0x2e, 0x53,
|
||||||
|
0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x4c,
|
||||||
|
0x65, 0x67, 0x61, 0x63, 0x79, 0x4d, 0x65, 0x74, 0x61, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0a,
|
||||||
|
0x4c, 0x65, 0x67, 0x61, 0x63, 0x79, 0x4d, 0x65, 0x74, 0x61, 0x12, 0x46, 0x0a, 0x10, 0x4c, 0x65,
|
||||||
|
0x67, 0x61, 0x63, 0x79, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, 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, 0x10, 0x4c, 0x65, 0x67, 0x61, 0x63, 0x79, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x54, 0x69,
|
||||||
|
0x6d, 0x65, 0x12, 0x46, 0x0a, 0x10, 0x4c, 0x65, 0x67, 0x61, 0x63, 0x79, 0x55, 0x70, 0x64, 0x61,
|
||||||
|
0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x18, 0x0a, 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, 0x10, 0x4c, 0x65, 0x67, 0x61, 0x63, 0x79,
|
||||||
|
0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x58, 0x0a, 0x0e, 0x45, 0x6e,
|
||||||
|
0x74, 0x65, 0x72, 0x70, 0x72, 0x69, 0x73, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x18, 0x0b, 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, 0x12, 0x0a, 0x04, 0x50, 0x65, 0x65, 0x72, 0x18, 0x0c, 0x20, 0x01,
|
||||||
|
0x28, 0x09, 0x52, 0x04, 0x50, 0x65, 0x65, 0x72, 0x1a, 0x3d, 0x0a, 0x0f, 0x4c, 0x65, 0x67, 0x61,
|
||||||
|
0x63, 0x79, 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, 0xb9, 0x01, 0x0a, 0x13, 0x49, 0x6e, 0x74, 0x65,
|
||||||
|
0x6e, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x12,
|
||||||
|
0x4e, 0x0a, 0x06, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 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, 0x63, 0x6f, 0x6e, 0x66,
|
||||||
|
0x69, 0x67, 0x65, 0x6e, 0x74, 0x72, 0x79, 0x2e, 0x49, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x69, 0x6f,
|
||||||
|
0x6e, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x06, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12,
|
||||||
|
0x52, 0x0a, 0x04, 0x48, 0x54, 0x54, 0x50, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x3e, 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, 0x66, 0x69, 0x67,
|
||||||
|
0x65, 0x6e, 0x74, 0x72, 0x79, 0x2e, 0x49, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x69, 0x6f, 0x6e, 0x48,
|
||||||
|
0x54, 0x54, 0x50, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x04, 0x48,
|
||||||
|
0x54, 0x54, 0x50, 0x22, 0xed, 0x01, 0x0a, 0x17, 0x49, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x69, 0x6f,
|
||||||
|
0x6e, 0x48, 0x54, 0x54, 0x50, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x12,
|
||||||
|
0x1c, 0x0a, 0x09, 0x50, 0x61, 0x74, 0x68, 0x45, 0x78, 0x61, 0x63, 0x74, 0x18, 0x01, 0x20, 0x01,
|
||||||
|
0x28, 0x09, 0x52, 0x09, 0x50, 0x61, 0x74, 0x68, 0x45, 0x78, 0x61, 0x63, 0x74, 0x12, 0x1e, 0x0a,
|
||||||
|
0x0a, 0x50, 0x61, 0x74, 0x68, 0x50, 0x72, 0x65, 0x66, 0x69, 0x78, 0x18, 0x02, 0x20, 0x01, 0x28,
|
||||||
|
0x09, 0x52, 0x0a, 0x50, 0x61, 0x74, 0x68, 0x50, 0x72, 0x65, 0x66, 0x69, 0x78, 0x12, 0x1c, 0x0a,
|
||||||
|
0x09, 0x50, 0x61, 0x74, 0x68, 0x52, 0x65, 0x67, 0x65, 0x78, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09,
|
||||||
|
0x52, 0x09, 0x50, 0x61, 0x74, 0x68, 0x52, 0x65, 0x67, 0x65, 0x78, 0x12, 0x5c, 0x0a, 0x06, 0x48,
|
||||||
|
0x65, 0x61, 0x64, 0x65, 0x72, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x44, 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, 0x66, 0x69, 0x67, 0x65, 0x6e,
|
||||||
|
0x74, 0x72, 0x79, 0x2e, 0x49, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x69, 0x6f, 0x6e, 0x48, 0x54, 0x54,
|
||||||
|
0x50, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f,
|
||||||
|
0x6e, 0x52, 0x06, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x12, 0x18, 0x0a, 0x07, 0x4d, 0x65, 0x74,
|
||||||
|
0x68, 0x6f, 0x64, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x4d, 0x65, 0x74, 0x68,
|
||||||
|
0x6f, 0x64, 0x73, 0x22, 0xc1, 0x01, 0x0a, 0x1d, 0x49, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x69, 0x6f,
|
||||||
|
0x6e, 0x48, 0x54, 0x54, 0x50, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x50, 0x65, 0x72, 0x6d, 0x69,
|
||||||
|
0x73, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20,
|
||||||
|
0x01, 0x28, 0x09, 0x52, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x50, 0x72, 0x65,
|
||||||
|
0x73, 0x65, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x50, 0x72, 0x65, 0x73,
|
||||||
|
0x65, 0x6e, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x45, 0x78, 0x61, 0x63, 0x74, 0x18, 0x03, 0x20, 0x01,
|
||||||
|
0x28, 0x09, 0x52, 0x05, 0x45, 0x78, 0x61, 0x63, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x50, 0x72, 0x65,
|
||||||
|
0x66, 0x69, 0x78, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x50, 0x72, 0x65, 0x66, 0x69,
|
||||||
|
0x78, 0x12, 0x16, 0x0a, 0x06, 0x53, 0x75, 0x66, 0x66, 0x69, 0x78, 0x18, 0x05, 0x20, 0x01, 0x28,
|
||||||
|
0x09, 0x52, 0x06, 0x53, 0x75, 0x66, 0x66, 0x69, 0x78, 0x12, 0x14, 0x0a, 0x05, 0x52, 0x65, 0x67,
|
||||||
|
0x65, 0x78, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x52, 0x65, 0x67, 0x65, 0x78, 0x12,
|
||||||
|
0x16, 0x0a, 0x06, 0x49, 0x6e, 0x76, 0x65, 0x72, 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 0x52,
|
||||||
|
0x06, 0x49, 0x6e, 0x76, 0x65, 0x72, 0x74, 0x2a, 0x77, 0x0a, 0x04, 0x4b, 0x69, 0x6e, 0x64, 0x12,
|
||||||
|
0x0f, 0x0a, 0x0b, 0x4b, 0x69, 0x6e, 0x64, 0x55, 0x6e, 0x6b, 0x6e, 0x6f, 0x77, 0x6e, 0x10, 0x00,
|
||||||
|
0x12, 0x12, 0x0a, 0x0e, 0x4b, 0x69, 0x6e, 0x64, 0x4d, 0x65, 0x73, 0x68, 0x43, 0x6f, 0x6e, 0x66,
|
||||||
|
0x69, 0x67, 0x10, 0x01, 0x12, 0x17, 0x0a, 0x13, 0x4b, 0x69, 0x6e, 0x64, 0x53, 0x65, 0x72, 0x76,
|
||||||
|
0x69, 0x63, 0x65, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x72, 0x10, 0x02, 0x12, 0x16, 0x0a,
|
||||||
|
0x12, 0x4b, 0x69, 0x6e, 0x64, 0x49, 0x6e, 0x67, 0x72, 0x65, 0x73, 0x73, 0x47, 0x61, 0x74, 0x65,
|
||||||
|
0x77, 0x61, 0x79, 0x10, 0x03, 0x12, 0x19, 0x0a, 0x15, 0x4b, 0x69, 0x6e, 0x64, 0x53, 0x65, 0x72,
|
||||||
|
0x76, 0x69, 0x63, 0x65, 0x49, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x10, 0x04,
|
||||||
|
0x2a, 0x26, 0x0a, 0x0f, 0x49, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x69, 0x6f, 0x6e, 0x41, 0x63, 0x74,
|
||||||
|
0x69, 0x6f, 0x6e, 0x12, 0x08, 0x0a, 0x04, 0x44, 0x65, 0x6e, 0x79, 0x10, 0x00, 0x12, 0x09, 0x0a,
|
||||||
|
0x05, 0x41, 0x6c, 0x6c, 0x6f, 0x77, 0x10, 0x01, 0x2a, 0x21, 0x0a, 0x13, 0x49, 0x6e, 0x74, 0x65,
|
||||||
|
0x6e, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x54, 0x79, 0x70, 0x65, 0x12,
|
||||||
|
0x0a, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x10, 0x00, 0x42, 0xa6, 0x02, 0x0a, 0x29,
|
||||||
|
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, 0x66, 0x69, 0x67, 0x65, 0x6e, 0x74, 0x72, 0x79, 0x42, 0x10, 0x43, 0x6f, 0x6e, 0x66, 0x69,
|
||||||
|
0x67, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x2f, 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, 0x66, 0x69, 0x67, 0x65, 0x6e, 0x74, 0x72, 0x79, 0xa2, 0x02,
|
||||||
|
0x04, 0x48, 0x43, 0x49, 0x43, 0xaa, 0x02, 0x25, 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, 0x66, 0x69, 0x67, 0x65, 0x6e, 0x74, 0x72, 0x79, 0xca, 0x02, 0x25,
|
||||||
|
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, 0x66, 0x69, 0x67,
|
||||||
|
0x65, 0x6e, 0x74, 0x72, 0x79, 0xe2, 0x02, 0x31, 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, 0x66, 0x69, 0x67, 0x65, 0x6e, 0x74, 0x72, 0x79, 0x5c, 0x47, 0x50,
|
||||||
|
0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x28, 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, 0x66, 0x69, 0x67, 0x65,
|
||||||
|
0x6e, 0x74, 0x72, 0x79, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
|
|
@ -122,6 +122,7 @@ message ServiceResolverRedirect {
|
||||||
string Namespace = 3;
|
string Namespace = 3;
|
||||||
string Partition = 4;
|
string Partition = 4;
|
||||||
string Datacenter = 5;
|
string Datacenter = 5;
|
||||||
|
string Peer = 6;
|
||||||
}
|
}
|
||||||
|
|
||||||
// mog annotation:
|
// mog annotation:
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
# Note this arg has to be before the first FROM
|
# Note this arg has to be before the first FROM
|
||||||
ARG ENVOY_VERSION
|
ARG ENVOY_VERSION
|
||||||
|
|
||||||
FROM consul-dev as consul
|
FROM consul:local as consul
|
||||||
|
|
||||||
FROM docker.mirror.hashicorp.services/envoyproxy/envoy:v${ENVOY_VERSION}
|
FROM docker.mirror.hashicorp.services/envoyproxy/envoy:v${ENVOY_VERSION}
|
||||||
COPY --from=consul /bin/consul /bin/consul
|
COPY --from=consul /bin/consul /bin/consul
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
primary_datacenter = "alpha"
|
||||||
|
log_level = "trace"
|
||||||
|
peering {
|
||||||
|
enabled = true
|
||||||
|
}
|
|
@ -0,0 +1,26 @@
|
||||||
|
config_entries {
|
||||||
|
bootstrap = [
|
||||||
|
{
|
||||||
|
kind = "proxy-defaults"
|
||||||
|
name = "global"
|
||||||
|
|
||||||
|
config {
|
||||||
|
protocol = "tcp"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
kind = "exported-services"
|
||||||
|
name = "default"
|
||||||
|
services = [
|
||||||
|
{
|
||||||
|
name = "s2"
|
||||||
|
consumers = [
|
||||||
|
{
|
||||||
|
peer_name = "alpha-to-primary"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
services {
|
||||||
|
name = "mesh-gateway"
|
||||||
|
kind = "mesh-gateway"
|
||||||
|
port = 4432
|
||||||
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
# We don't want an s1 service in this peer
|
|
@ -0,0 +1,7 @@
|
||||||
|
services {
|
||||||
|
name = "s2"
|
||||||
|
port = 8181
|
||||||
|
connect {
|
||||||
|
sidecar_service {}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
register_services alpha
|
||||||
|
|
||||||
|
gen_envoy_bootstrap s2 19002 alpha
|
||||||
|
gen_envoy_bootstrap mesh-gateway 19003 alpha true
|
||||||
|
|
||||||
|
wait_for_config_entry proxy-defaults global alpha
|
||||||
|
wait_for_config_entry exported-services default alpha
|
|
@ -0,0 +1,27 @@
|
||||||
|
#!/usr/bin/env bats
|
||||||
|
|
||||||
|
load helpers
|
||||||
|
|
||||||
|
@test "s2 proxy is running correct version" {
|
||||||
|
assert_envoy_version 19002
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "s2 proxy admin is up on :19002" {
|
||||||
|
retry_default curl -f -s localhost:19002/stats -o /dev/null
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "gateway-alpha proxy admin is up on :19003" {
|
||||||
|
retry_default curl -f -s localhost:19003/stats -o /dev/null
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "s2 proxy listener should be up and have right cert" {
|
||||||
|
assert_proxy_presents_cert_uri localhost:21000 s2 alpha
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "s2 proxy should be healthy" {
|
||||||
|
assert_service_has_healthy_instances s2 1 alpha
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "gateway-alpha should be up and listening" {
|
||||||
|
retry_long nc -z consul-alpha-client:4432
|
||||||
|
}
|
|
@ -0,0 +1,2 @@
|
||||||
|
bind_addr = "0.0.0.0"
|
||||||
|
advertise_addr = "{{ GetInterfaceIP \"eth0\" }}"
|
|
@ -0,0 +1,6 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
snapshot_envoy_admin localhost:19000 s1 primary || true
|
||||||
|
snapshot_envoy_admin localhost:19001 s2 primary || true
|
||||||
|
snapshot_envoy_admin localhost:19002 s2 alpha || true
|
||||||
|
snapshot_envoy_admin localhost:19003 mesh-gateway alpha || true
|
|
@ -0,0 +1,3 @@
|
||||||
|
peering {
|
||||||
|
enabled = true
|
||||||
|
}
|
|
@ -0,0 +1,21 @@
|
||||||
|
config_entries {
|
||||||
|
bootstrap {
|
||||||
|
kind = "proxy-defaults"
|
||||||
|
name = "global"
|
||||||
|
|
||||||
|
config {
|
||||||
|
protocol = "tcp"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bootstrap {
|
||||||
|
kind = "service-resolver"
|
||||||
|
name = "s2"
|
||||||
|
|
||||||
|
failover = {
|
||||||
|
"*" = {
|
||||||
|
targets = [{peer = "primary-to-alpha"}]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,16 @@
|
||||||
|
services {
|
||||||
|
name = "s1"
|
||||||
|
port = 8080
|
||||||
|
connect {
|
||||||
|
sidecar_service {
|
||||||
|
proxy {
|
||||||
|
upstreams = [
|
||||||
|
{
|
||||||
|
destination_name = "s2"
|
||||||
|
local_bind_port = 5000
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
services {
|
||||||
|
name = "s2"
|
||||||
|
port = 8181
|
||||||
|
connect {
|
||||||
|
sidecar_service {}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,10 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
register_services primary
|
||||||
|
|
||||||
|
gen_envoy_bootstrap s1 19000 primary
|
||||||
|
gen_envoy_bootstrap s2 19001 primary
|
||||||
|
|
||||||
|
wait_for_config_entry proxy-defaults global
|
|
@ -0,0 +1,87 @@
|
||||||
|
#!/usr/bin/env bats
|
||||||
|
|
||||||
|
load helpers
|
||||||
|
|
||||||
|
@test "s1 proxy is running correct version" {
|
||||||
|
assert_envoy_version 19000
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "s1 proxy admin is up on :19000" {
|
||||||
|
retry_default curl -f -s localhost:19000/stats -o /dev/null
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "s2 proxy admin is up on :19001" {
|
||||||
|
retry_default curl -f -s localhost:19001/stats -o /dev/null
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "gateway-primary proxy admin is up on :19001" {
|
||||||
|
retry_default curl localhost:19000/config_dump
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "s1 proxy listener should be up and have right cert" {
|
||||||
|
assert_proxy_presents_cert_uri localhost:21000 s1
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "s2 proxies should be healthy in primary" {
|
||||||
|
assert_service_has_healthy_instances s2 1 primary
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "s2 proxies should be healthy in alpha" {
|
||||||
|
assert_service_has_healthy_instances s2 1 alpha
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "gateway-alpha should be up and listening" {
|
||||||
|
retry_long nc -z consul-alpha-client:4432
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "peer the two clusters together" {
|
||||||
|
create_peering primary alpha
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "s2 alpha proxies should be healthy in primary" {
|
||||||
|
assert_service_has_healthy_instances s2 1 primary "" "" primary-to-alpha
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "s1 upstream should have healthy endpoints for s2 in both primary and failover" {
|
||||||
|
assert_upstream_has_endpoints_in_status 127.0.0.1:19000 failover-target~s2.default.primary.internal HEALTHY 1
|
||||||
|
assert_upstream_has_endpoints_in_status 127.0.0.1:19000 failover-target~s2.default.primary-to-alpha.external HEALTHY 1
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@test "s1 upstream should be able to connect to s2" {
|
||||||
|
run retry_default curl -s -f -d hello localhost:5000
|
||||||
|
[ "$status" -eq 0 ]
|
||||||
|
[ "$output" = "hello" ]
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "s1 upstream made 1 connection" {
|
||||||
|
assert_envoy_metric_at_least 127.0.0.1:19000 "cluster.failover-target~s2.default.primary.internal.*cx_total" 1
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "terminate instance of s2 primary envoy which should trigger failover to s2 alpha when the tcp check fails" {
|
||||||
|
kill_envoy s2 primary
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "s2 proxies should be unhealthy in primary" {
|
||||||
|
assert_service_has_healthy_instances s2 0 primary
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "s1 upstream should have healthy endpoints for s2 in the failover cluster peer" {
|
||||||
|
assert_upstream_has_endpoints_in_status 127.0.0.1:19000 failover-target~s2.default.primary.internal UNHEALTHY 1
|
||||||
|
assert_upstream_has_endpoints_in_status 127.0.0.1:19000 failover-target~s2.default.primary-to-alpha.external HEALTHY 1
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "reset envoy statistics" {
|
||||||
|
reset_envoy_metrics 127.0.0.1:19000
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@test "s1 upstream should be able to connect to s2 in the failover cluster peer" {
|
||||||
|
run retry_default curl -s -f -d hello localhost:5000
|
||||||
|
[ "$status" -eq 0 ]
|
||||||
|
[ "$output" = "hello" ]
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "s1 upstream made 1 connection to s2 through the cluster peer" {
|
||||||
|
assert_envoy_metric_at_least 127.0.0.1:19000 "cluster.failover-target~s2.default.primary-to-alpha.external.*cx_total" 1
|
||||||
|
}
|
|
@ -0,0 +1,4 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
export REQUIRED_SERVICES="s1 s1-sidecar-proxy s2 s2-sidecar-proxy s2-alpha s2-sidecar-proxy-alpha gateway-alpha tcpdump-primary tcpdump-alpha"
|
||||||
|
export REQUIRE_PEERS=1
|
|
@ -17,7 +17,7 @@ consul tls cert create -dc=secondary -server -node=sec
|
||||||
"
|
"
|
||||||
|
|
||||||
docker rm -f "$container" &>/dev/null || true
|
docker rm -f "$container" &>/dev/null || true
|
||||||
docker run -i --net=none --name="$container" consul-dev:latest sh -c "${scriptlet}"
|
docker run -i --net=none --name="$container" consul:local sh -c "${scriptlet}"
|
||||||
|
|
||||||
# primary
|
# primary
|
||||||
for f in \
|
for f in \
|
||||||
|
|
|
@ -562,14 +562,14 @@ function assert_intention_denied {
|
||||||
function docker_consul {
|
function docker_consul {
|
||||||
local DC=$1
|
local DC=$1
|
||||||
shift 1
|
shift 1
|
||||||
docker run -i --rm --network container:envoy_consul-${DC}_1 consul-dev "$@"
|
docker run -i --rm --network container:envoy_consul-${DC}_1 consul:local "$@"
|
||||||
}
|
}
|
||||||
|
|
||||||
function docker_consul_for_proxy_bootstrap {
|
function docker_consul_for_proxy_bootstrap {
|
||||||
local DC=$1
|
local DC=$1
|
||||||
shift 1
|
shift 1
|
||||||
|
|
||||||
docker run -i --rm --network container:envoy_consul-${DC}_1 consul-dev "$@"
|
docker run -i --rm --network container:envoy_consul-${DC}_1 consul:local "$@" 2> /dev/null
|
||||||
}
|
}
|
||||||
|
|
||||||
function docker_wget {
|
function docker_wget {
|
||||||
|
@ -581,7 +581,7 @@ function docker_wget {
|
||||||
function docker_curl {
|
function docker_curl {
|
||||||
local DC=$1
|
local DC=$1
|
||||||
shift 1
|
shift 1
|
||||||
docker run --rm --network container:envoy_consul-${DC}_1 --entrypoint curl consul-dev "$@"
|
docker run --rm --network container:envoy_consul-${DC}_1 --entrypoint curl consul:local "$@"
|
||||||
}
|
}
|
||||||
|
|
||||||
function docker_exec {
|
function docker_exec {
|
||||||
|
@ -806,9 +806,16 @@ function delete_config_entry {
|
||||||
|
|
||||||
function register_services {
|
function register_services {
|
||||||
local DC=${1:-primary}
|
local DC=${1:-primary}
|
||||||
|
wait_for_leader "$DC"
|
||||||
docker_consul_exec ${DC} sh -c "consul services register /workdir/${DC}/register/service_*.hcl"
|
docker_consul_exec ${DC} sh -c "consul services register /workdir/${DC}/register/service_*.hcl"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# wait_for_leader waits until a leader is elected.
|
||||||
|
# Its first argument must be the datacenter name.
|
||||||
|
function wait_for_leader {
|
||||||
|
retry_default docker_consul_exec "$1" sh -c '[[ $(curl --fail -sS http://127.0.0.1:8500/v1/status/leader) ]]'
|
||||||
|
}
|
||||||
|
|
||||||
function setup_upsert_l4_intention {
|
function setup_upsert_l4_intention {
|
||||||
local SOURCE=$1
|
local SOURCE=$1
|
||||||
local DESTINATION=$2
|
local DESTINATION=$2
|
||||||
|
|
|
@ -16,6 +16,8 @@ ENVOY_VERSION=${ENVOY_VERSION:-"1.23.0"}
|
||||||
export ENVOY_VERSION
|
export ENVOY_VERSION
|
||||||
|
|
||||||
export DOCKER_BUILDKIT=1
|
export DOCKER_BUILDKIT=1
|
||||||
|
# Always run tests on amd64 because that's what the CI environment uses.
|
||||||
|
export DOCKER_DEFAULT_PLATFORM="linux/amd64"
|
||||||
|
|
||||||
if [ ! -z "$DEBUG" ] ; then
|
if [ ! -z "$DEBUG" ] ; then
|
||||||
set -x
|
set -x
|
||||||
|
@ -44,17 +46,19 @@ function network_snippet {
|
||||||
}
|
}
|
||||||
|
|
||||||
function aws_snippet {
|
function aws_snippet {
|
||||||
local snippet=""
|
if [[ ! -z "$LAMBDA_TESTS_ENABLED" ]]; then
|
||||||
|
local snippet=""
|
||||||
|
|
||||||
# The Lambda integration cases assume that a Lambda function exists in $AWS_REGION with an ARN of $AWS_LAMBDA_ARN.
|
# The Lambda integration cases assume that a Lambda function exists in $AWS_REGION with an ARN of $AWS_LAMBDA_ARN.
|
||||||
# The AWS credentials must have permission to invoke the Lambda function.
|
# The AWS credentials must have permission to invoke the Lambda function.
|
||||||
[ -n "$(set | grep '^AWS_ACCESS_KEY_ID=')" ] && snippet="${snippet} -e AWS_ACCESS_KEY_ID=$AWS_ACCESS_KEY_ID"
|
[ -n "$(set | grep '^AWS_ACCESS_KEY_ID=')" ] && snippet="${snippet} -e AWS_ACCESS_KEY_ID=$AWS_ACCESS_KEY_ID"
|
||||||
[ -n "$(set | grep '^AWS_SECRET_ACCESS_KEY=')" ] && snippet="${snippet} -e AWS_SECRET_ACCESS_KEY=$AWS_SECRET_ACCESS_KEY"
|
[ -n "$(set | grep '^AWS_SECRET_ACCESS_KEY=')" ] && snippet="${snippet} -e AWS_SECRET_ACCESS_KEY=$AWS_SECRET_ACCESS_KEY"
|
||||||
[ -n "$(set | grep '^AWS_SESSION_TOKEN=')" ] && snippet="${snippet} -e AWS_SESSION_TOKEN=$AWS_SESSION_TOKEN"
|
[ -n "$(set | grep '^AWS_SESSION_TOKEN=')" ] && snippet="${snippet} -e AWS_SESSION_TOKEN=$AWS_SESSION_TOKEN"
|
||||||
[ -n "$(set | grep '^AWS_LAMBDA_REGION=')" ] && snippet="${snippet} -e AWS_LAMBDA_REGION=$AWS_LAMBDA_REGION"
|
[ -n "$(set | grep '^AWS_LAMBDA_REGION=')" ] && snippet="${snippet} -e AWS_LAMBDA_REGION=$AWS_LAMBDA_REGION"
|
||||||
[ -n "$(set | grep '^AWS_LAMBDA_ARN=')" ] && snippet="${snippet} -e AWS_LAMBDA_ARN=$AWS_LAMBDA_ARN"
|
[ -n "$(set | grep '^AWS_LAMBDA_ARN=')" ] && snippet="${snippet} -e AWS_LAMBDA_ARN=$AWS_LAMBDA_ARN"
|
||||||
|
|
||||||
echo "$snippet"
|
echo "$snippet"
|
||||||
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
function init_workdir {
|
function init_workdir {
|
||||||
|
@ -222,7 +226,7 @@ function start_consul {
|
||||||
--hostname "consul-${DC}-server" \
|
--hostname "consul-${DC}-server" \
|
||||||
--network-alias "consul-${DC}-server" \
|
--network-alias "consul-${DC}-server" \
|
||||||
-e "CONSUL_LICENSE=$license" \
|
-e "CONSUL_LICENSE=$license" \
|
||||||
consul-dev \
|
consul:local \
|
||||||
agent -dev -datacenter "${DC}" \
|
agent -dev -datacenter "${DC}" \
|
||||||
-config-dir "/workdir/${DC}/consul" \
|
-config-dir "/workdir/${DC}/consul" \
|
||||||
-config-dir "/workdir/${DC}/consul-server" \
|
-config-dir "/workdir/${DC}/consul-server" \
|
||||||
|
@ -237,7 +241,7 @@ function start_consul {
|
||||||
--network-alias "consul-${DC}-client" \
|
--network-alias "consul-${DC}-client" \
|
||||||
-e "CONSUL_LICENSE=$license" \
|
-e "CONSUL_LICENSE=$license" \
|
||||||
${ports[@]} \
|
${ports[@]} \
|
||||||
consul-dev \
|
consul:local \
|
||||||
agent -datacenter "${DC}" \
|
agent -datacenter "${DC}" \
|
||||||
-config-dir "/workdir/${DC}/consul" \
|
-config-dir "/workdir/${DC}/consul" \
|
||||||
-data-dir "/tmp/consul" \
|
-data-dir "/tmp/consul" \
|
||||||
|
@ -256,7 +260,7 @@ function start_consul {
|
||||||
--network-alias "consul-${DC}-server" \
|
--network-alias "consul-${DC}-server" \
|
||||||
-e "CONSUL_LICENSE=$license" \
|
-e "CONSUL_LICENSE=$license" \
|
||||||
${ports[@]} \
|
${ports[@]} \
|
||||||
consul-dev \
|
consul:local \
|
||||||
agent -dev -datacenter "${DC}" \
|
agent -dev -datacenter "${DC}" \
|
||||||
-config-dir "/workdir/${DC}/consul" \
|
-config-dir "/workdir/${DC}/consul" \
|
||||||
-config-dir "/workdir/${DC}/consul-server" \
|
-config-dir "/workdir/${DC}/consul-server" \
|
||||||
|
@ -290,7 +294,7 @@ function start_partitioned_client {
|
||||||
--hostname "consul-${PARTITION}-client" \
|
--hostname "consul-${PARTITION}-client" \
|
||||||
--network-alias "consul-${PARTITION}-client" \
|
--network-alias "consul-${PARTITION}-client" \
|
||||||
-e "CONSUL_LICENSE=$license" \
|
-e "CONSUL_LICENSE=$license" \
|
||||||
consul-dev agent \
|
consul:local agent \
|
||||||
-datacenter "primary" \
|
-datacenter "primary" \
|
||||||
-retry-join "consul-primary-server" \
|
-retry-join "consul-primary-server" \
|
||||||
-grpc-port 8502 \
|
-grpc-port 8502 \
|
||||||
|
|
|
@ -168,7 +168,8 @@ Usage: `consul snapshot agent [options]`
|
||||||
"s3_bucket": "",
|
"s3_bucket": "",
|
||||||
"s3_key_prefix": "consul-snapshot",
|
"s3_key_prefix": "consul-snapshot",
|
||||||
"s3_server_side_encryption": false,
|
"s3_server_side_encryption": false,
|
||||||
"s3_static_snapshot_name": ""
|
"s3_static_snapshot_name": "",
|
||||||
|
"s3_force_path_style": false
|
||||||
},
|
},
|
||||||
"azure_blob_storage": {
|
"azure_blob_storage": {
|
||||||
"account_name": "",
|
"account_name": "",
|
||||||
|
@ -275,6 +276,10 @@ Note that despite the AWS references, any S3-compatible endpoint can be specifie
|
||||||
- `-aws-s3-static-snapshot-name` - If this is given, all snapshots are saved with the same file name. The agent will not rotate or version snapshots, and will save them with the same name each time.
|
- `-aws-s3-static-snapshot-name` - If this is given, all snapshots are saved with the same file name. The agent will not rotate or version snapshots, and will save them with the same name each time.
|
||||||
Use this if you want to rely on [S3's versioning capabilities](http://docs.aws.amazon.com/AmazonS3/latest/dev/Versioning.html) instead of the agent handling it for you.
|
Use this if you want to rely on [S3's versioning capabilities](http://docs.aws.amazon.com/AmazonS3/latest/dev/Versioning.html) instead of the agent handling it for you.
|
||||||
|
|
||||||
|
- `-aws-s3-force-path-style` - Enables the use of legacy path-based addressing instead of virtual addressing. This flag is required by minio
|
||||||
|
and other 3rd party S3 compatible object storage platforms where DNS or TLS requirements for virtual addressing are prohibitive.
|
||||||
|
For more information, refer to the AWS documentation on [Methods for accessing a bucket](https://docs.aws.amazon.com/AmazonS3/latest/userguide/access-bucket-intro.html)
|
||||||
|
|
||||||
- `-aws-s3-enable-kms` - Enables using [Amazon KMS](https://aws.amazon.com/kms/) for encrypting snapshots.
|
- `-aws-s3-enable-kms` - Enables using [Amazon KMS](https://aws.amazon.com/kms/) for encrypting snapshots.
|
||||||
|
|
||||||
- `-aws-s3-kms-key` - Optional Amazon KMS key to use, if this is not set the default KMS master key will be used. Set this if you want to manage key rotation yourself.
|
- `-aws-s3-kms-key` - Optional Amazon KMS key to use, if this is not set the default KMS master key will be used. Set this if you want to manage key rotation yourself.
|
||||||
|
|
|
@ -349,59 +349,59 @@ populated free list structure.
|
||||||
|
|
||||||
This is a full list of metrics emitted by Consul.
|
This is a full list of metrics emitted by Consul.
|
||||||
|
|
||||||
| Metric | Description | Unit | Type |
|
| Metric | Description | Unit | Type |
|
||||||
| -------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------- | ------- |
|
|--------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|----------------------|---------|
|
||||||
| `consul.acl.blocked.{check,service}.deregistration` | Increments whenever a deregistration fails for an entity (check or service) is blocked by an ACL. | requests | counter |
|
| `consul.acl.blocked.{check,service}.deregistration` | Increments whenever a deregistration fails for an entity (check or service) is blocked by an ACL. | requests | counter |
|
||||||
| `consul.acl.blocked.{check,node,service}.registration` | Increments whenever a registration fails for an entity (check, node or service) is blocked by an ACL. | requests | counter |
|
| `consul.acl.blocked.{check,node,service}.registration` | Increments whenever a registration fails for an entity (check, node or service) is blocked by an ACL. | requests | counter |
|
||||||
| `consul.api.http` | This samples how long it takes to service the given HTTP request for the given verb and path. Includes labels for `path` and `method`. `path` does not include details like service or key names, for these an underscore will be present as a placeholder (eg. path=`v1.kv._`) | ms | timer |
|
| `consul.api.http` | This samples how long it takes to service the given HTTP request for the given verb and path. Includes labels for `path` and `method`. `path` does not include details like service or key names, for these an underscore will be present as a placeholder (eg. path=`v1.kv._`) | ms | timer |
|
||||||
| `consul.client.rpc` | Increments whenever a Consul agent in client mode makes an RPC request to a Consul server. This gives a measure of how much a given agent is loading the Consul servers. Currently, this is only generated by agents in client mode, not Consul servers. | requests | counter |
|
| `consul.client.rpc` | Increments whenever a Consul agent in client mode makes an RPC request to a Consul server. This gives a measure of how much a given agent is loading the Consul servers. Currently, this is only generated by agents in client mode, not Consul servers. | requests | counter |
|
||||||
| `consul.client.rpc.exceeded` | Increments whenever a Consul agent in client mode makes an RPC request to a Consul server gets rate limited by that agent's [`limits`](/docs/agent/config/config-files#limits) configuration. This gives an indication that there's an abusive application making too many requests on the agent, or that the rate limit needs to be increased. Currently, this only applies to agents in client mode, not Consul servers. | rejected requests | counter |
|
| `consul.client.rpc.exceeded` | Increments whenever a Consul agent in client mode makes an RPC request to a Consul server gets rate limited by that agent's [`limits`](/docs/agent/config/config-files#limits) configuration. This gives an indication that there's an abusive application making too many requests on the agent, or that the rate limit needs to be increased. Currently, this only applies to agents in client mode, not Consul servers. | rejected requests | counter |
|
||||||
| `consul.client.rpc.failed` | Increments whenever a Consul agent in client mode makes an RPC request to a Consul server and fails. | requests | counter |
|
| `consul.client.rpc.failed` | Increments whenever a Consul agent in client mode makes an RPC request to a Consul server and fails. | requests | counter |
|
||||||
| `consul.client.api.catalog_register.` | Increments whenever a Consul agent receives a catalog register request. | requests | counter |
|
| `consul.client.api.catalog_register.` | Increments whenever a Consul agent receives a catalog register request. | requests | counter |
|
||||||
| `consul.client.api.success.catalog_register.` | Increments whenever a Consul agent successfully responds to a catalog register request. | requests | counter |
|
| `consul.client.api.success.catalog_register.` | Increments whenever a Consul agent successfully responds to a catalog register request. | requests | counter |
|
||||||
| `consul.client.rpc.error.catalog_register.` | Increments whenever a Consul agent receives an RPC error for a catalog register request. | errors | counter |
|
| `consul.client.rpc.error.catalog_register.` | Increments whenever a Consul agent receives an RPC error for a catalog register request. | errors | counter |
|
||||||
| `consul.client.api.catalog_deregister.` | Increments whenever a Consul agent receives a catalog deregister request. | requests | counter |
|
| `consul.client.api.catalog_deregister.` | Increments whenever a Consul agent receives a catalog deregister request. | requests | counter |
|
||||||
| `consul.client.api.success.catalog_deregister.` | Increments whenever a Consul agent successfully responds to a catalog deregister request. | requests | counter |
|
| `consul.client.api.success.catalog_deregister.` | Increments whenever a Consul agent successfully responds to a catalog deregister request. | requests | counter |
|
||||||
| `consul.client.rpc.error.catalog_deregister.` | Increments whenever a Consul agent receives an RPC error for a catalog deregister request. | errors | counter |
|
| `consul.client.rpc.error.catalog_deregister.` | Increments whenever a Consul agent receives an RPC error for a catalog deregister request. | errors | counter |
|
||||||
| `consul.client.api.catalog_datacenters.` | Increments whenever a Consul agent receives a request to list datacenters in the catalog. | requests | counter |
|
| `consul.client.api.catalog_datacenters.` | Increments whenever a Consul agent receives a request to list datacenters in the catalog. | requests | counter |
|
||||||
| `consul.client.api.success.catalog_datacenters.` | Increments whenever a Consul agent successfully responds to a request to list datacenters. | requests | counter |
|
| `consul.client.api.success.catalog_datacenters.` | Increments whenever a Consul agent successfully responds to a request to list datacenters. | requests | counter |
|
||||||
| `consul.client.rpc.error.catalog_datacenters.` | Increments whenever a Consul agent receives an RPC error for a request to list datacenters. | errors | counter |
|
| `consul.client.rpc.error.catalog_datacenters.` | Increments whenever a Consul agent receives an RPC error for a request to list datacenters. | errors | counter |
|
||||||
| `consul.client.api.catalog_nodes.` | Increments whenever a Consul agent receives a request to list nodes from the catalog. | requests | counter |
|
| `consul.client.api.catalog_nodes.` | Increments whenever a Consul agent receives a request to list nodes from the catalog. | requests | counter |
|
||||||
| `consul.client.api.success.catalog_nodes.` | Increments whenever a Consul agent successfully responds to a request to list nodes. | requests | counter |
|
| `consul.client.api.success.catalog_nodes.` | Increments whenever a Consul agent successfully responds to a request to list nodes. | requests | counter |
|
||||||
| `consul.client.rpc.error.catalog_nodes.` | Increments whenever a Consul agent receives an RPC error for a request to list nodes. | errors | counter |
|
| `consul.client.rpc.error.catalog_nodes.` | Increments whenever a Consul agent receives an RPC error for a request to list nodes. | errors | counter |
|
||||||
| `consul.client.api.catalog_services.` | Increments whenever a Consul agent receives a request to list services from the catalog. | requests | counter |
|
| `consul.client.api.catalog_services.` | Increments whenever a Consul agent receives a request to list services from the catalog. | requests | counter |
|
||||||
| `consul.client.api.success.catalog_services.` | Increments whenever a Consul agent successfully responds to a request to list services. | requests | counter |
|
| `consul.client.api.success.catalog_services.` | Increments whenever a Consul agent successfully responds to a request to list services. | requests | counter |
|
||||||
| `consul.client.rpc.error.catalog_services.` | Increments whenever a Consul agent receives an RPC error for a request to list services. | errors | counter |
|
| `consul.client.rpc.error.catalog_services.` | Increments whenever a Consul agent receives an RPC error for a request to list services. | errors | counter |
|
||||||
| `consul.client.api.catalog_service_nodes.` | Increments whenever a Consul agent receives a request to list nodes offering a service. | requests | counter |
|
| `consul.client.api.catalog_service_nodes.` | Increments whenever a Consul agent receives a request to list nodes offering a service. | requests | counter |
|
||||||
| `consul.client.api.success.catalog_service_nodes.` | Increments whenever a Consul agent successfully responds to a request to list nodes offering a service. | requests | counter |
|
| `consul.client.api.success.catalog_service_nodes.` | Increments whenever a Consul agent successfully responds to a request to list nodes offering a service. | requests | counter |
|
||||||
| `consul.client.api.error.catalog_service_nodes.` | Increments whenever a Consul agent receives an RPC error for request to list nodes offering a service. | requests | counter |
|
| `consul.client.api.error.catalog_service_nodes.` | Increments whenever a Consul agent receives an RPC error for request to list nodes offering a service. | requests | counter |
|
||||||
| `consul.client.rpc.error.catalog_service_nodes.` | Increments whenever a Consul agent receives an RPC error for a request to list nodes offering a service. | errors | counter |
|
| `consul.client.rpc.error.catalog_service_nodes.` | Increments whenever a Consul agent receives an RPC error for a request to list nodes offering a service. | errors | counter |
|
||||||
| `consul.client.api.catalog_node_services.` | Increments whenever a Consul agent receives a request to list services registered in a node. | requests | counter |
|
| `consul.client.api.catalog_node_services.` | Increments whenever a Consul agent receives a request to list services registered in a node. | requests | counter |
|
||||||
| `consul.client.api.success.catalog_node_services.` | Increments whenever a Consul agent successfully responds to a request to list services in a node. | requests | counter |
|
| `consul.client.api.success.catalog_node_services.` | Increments whenever a Consul agent successfully responds to a request to list services in a node. | requests | counter |
|
||||||
| `consul.client.rpc.error.catalog_node_services.` | Increments whenever a Consul agent receives an RPC error for a request to list services in a node. | errors | counter |
|
| `consul.client.rpc.error.catalog_node_services.` | Increments whenever a Consul agent receives an RPC error for a request to list services in a node. | errors | counter |
|
||||||
| `consul.client.api.catalog_node_service_list` | Increments whenever a Consul agent receives a request to list a node's registered services. | requests | counter |
|
| `consul.client.api.catalog_node_service_list` | Increments whenever a Consul agent receives a request to list a node's registered services. | requests | counter |
|
||||||
| `consul.client.rpc.error.catalog_node_service_list` | Increments whenever a Consul agent receives an RPC error for request to list a node's registered services. | errors | counter |
|
| `consul.client.rpc.error.catalog_node_service_list` | Increments whenever a Consul agent receives an RPC error for request to list a node's registered services. | errors | counter |
|
||||||
| `consul.client.api.success.catalog_node_service_list` | Increments whenever a Consul agent successfully responds to a request to list a node's registered services. | requests | counter |
|
| `consul.client.api.success.catalog_node_service_list` | Increments whenever a Consul agent successfully responds to a request to list a node's registered services. | requests | counter |
|
||||||
| `consul.client.api.catalog_gateway_services.` | Increments whenever a Consul agent receives a request to list services associated with a gateway. | requests | counter |
|
| `consul.client.api.catalog_gateway_services.` | Increments whenever a Consul agent receives a request to list services associated with a gateway. | requests | counter |
|
||||||
| `consul.client.api.success.catalog_gateway_services.` | Increments whenever a Consul agent successfully responds to a request to list services associated with a gateway. | requests | counter |
|
| `consul.client.api.success.catalog_gateway_services.` | Increments whenever a Consul agent successfully responds to a request to list services associated with a gateway. | requests | counter |
|
||||||
| `consul.client.rpc.error.catalog_gateway_services.` | Increments whenever a Consul agent receives an RPC error for a request to list services associated with a gateway. | errors | counter |
|
| `consul.client.rpc.error.catalog_gateway_services.` | Increments whenever a Consul agent receives an RPC error for a request to list services associated with a gateway. | errors | counter |
|
||||||
| `consul.runtime.num_goroutines` | Tracks the number of running goroutines and is a general load pressure indicator. This may burst from time to time but should return to a steady state value. | number of goroutines | gauge |
|
| `consul.runtime.num_goroutines` | Tracks the number of running goroutines and is a general load pressure indicator. This may burst from time to time but should return to a steady state value. | number of goroutines | gauge |
|
||||||
| `consul.runtime.alloc_bytes` | Measures the number of bytes allocated by the Consul process. This may burst from time to time but should return to a steady state value. | bytes | gauge |
|
| `consul.runtime.alloc_bytes` | Measures the number of bytes allocated by the Consul process. This may burst from time to time but should return to a steady state value. | bytes | gauge |
|
||||||
| `consul.runtime.heap_objects` | Measures the number of objects allocated on the heap and is a general memory pressure indicator. This may burst from time to time but should return to a steady state value. | number of objects | gauge |
|
| `consul.runtime.heap_objects` | Measures the number of objects allocated on the heap and is a general memory pressure indicator. This may burst from time to time but should return to a steady state value. | number of objects | gauge |
|
||||||
| `consul.state.nodes` | Measures the current number of nodes registered with Consul. It is only emitted by Consul servers. Added in v1.9.0. | number of objects | gauge |
|
| `consul.state.nodes` | Measures the current number of nodes registered with Consul. It is only emitted by Consul servers. Added in v1.9.0. | number of objects | gauge |
|
||||||
| `consul.state.peerings` | Measures the current number of peerings registered with Consul. It is only emitted by Consul servers. Added in v1.13.0. | number of objects | gauge |
|
| `consul.state.peerings` | Measures the current number of peerings registered with Consul. It is only emitted by Consul servers. Added in v1.13.0. | number of objects | gauge |
|
||||||
| `consul.state.services` | Measures the current number of unique services registered with Consul, based on service name. It is only emitted by Consul servers. Added in v1.9.0. | number of objects | gauge |
|
| `consul.state.services` | Measures the current number of unique services registered with Consul, based on service name. It is only emitted by Consul servers. Added in v1.9.0. | number of objects | gauge |
|
||||||
| `consul.state.service_instances` | Measures the current number of unique service instances registered with Consul. It is only emitted by Consul servers. Added in v1.9.0. | number of objects | gauge |
|
| `consul.state.service_instances` | Measures the current number of unique service instances registered with Consul. It is only emitted by Consul servers. Added in v1.9.0. | number of objects | gauge |
|
||||||
| `consul.state.kv_entries` | Measures the current number of unique KV entries written in Consul. It is only emitted by Consul servers. Added in v1.10.3. | number of objects | gauge |
|
| `consul.state.kv_entries` | Measures the current number of entries in the Consul KV store. It is only emitted by Consul servers. Added in v1.10.3. | number of objects | gauge |
|
||||||
| `consul.state.connect_instances` | Measures the current number of unique connect service instances registered with Consul labeled by Kind (e.g. connect-proxy, connect-native, etc). Added in v1.10.4 | number of objects | gauge |
|
| `consul.state.connect_instances` | Measures the current number of unique connect service instances registered with Consul labeled by Kind (e.g. connect-proxy, connect-native, etc). Added in v1.10.4 | number of objects | gauge |
|
||||||
| `consul.state.config_entries` | Measures the current number of configuration entries registered with Consul labeled by Kind (e.g. service-defaults, proxy-defaults, etc). See [Configuration Entries](/docs/connect/config-entries) for more information. Added in v1.10.4 | number of objects | gauge |
|
| `consul.state.config_entries` | Measures the current number of configuration entries registered with Consul labeled by Kind (e.g. service-defaults, proxy-defaults, etc). See [Configuration Entries](/docs/connect/config-entries) for more information. Added in v1.10.4 | number of objects | gauge |
|
||||||
| `consul.members.clients` | Measures the current number of client agents registered with Consul. It is only emitted by Consul servers. Added in v1.9.6. | number of clients | gauge |
|
| `consul.members.clients` | Measures the current number of client agents registered with Consul. It is only emitted by Consul servers. Added in v1.9.6. | number of clients | gauge |
|
||||||
| `consul.members.servers` | Measures the current number of server agents registered with Consul. It is only emitted by Consul servers. Added in v1.9.6. | number of servers | gauge |
|
| `consul.members.servers` | Measures the current number of server agents registered with Consul. It is only emitted by Consul servers. Added in v1.9.6. | number of servers | gauge |
|
||||||
| `consul.dns.stale_queries` | Increments when an agent serves a query within the allowed stale threshold. | queries | counter |
|
| `consul.dns.stale_queries` | Increments when an agent serves a query within the allowed stale threshold. | queries | counter |
|
||||||
| `consul.dns.ptr_query.` | Measures the time spent handling a reverse DNS query for the given node. | ms | timer |
|
| `consul.dns.ptr_query.` | Measures the time spent handling a reverse DNS query for the given node. | ms | timer |
|
||||||
| `consul.dns.domain_query.` | Measures the time spent handling a domain query for the given node. | ms | timer |
|
| `consul.dns.domain_query.` | Measures the time spent handling a domain query for the given node. | ms | timer |
|
||||||
| `consul.system.licenseExpiration` | <EnterpriseAlert inline /> This measures the number of hours remaining on the agents license. | hours | gauge |
|
| `consul.system.licenseExpiration` | <EnterpriseAlert inline /> This measures the number of hours remaining on the agents license. | hours | gauge |
|
||||||
| `consul.version` | Represents the Consul version. | agents | gauge |
|
| `consul.version` | Represents the Consul version. | agents | gauge |
|
||||||
|
|
||||||
## Server Health
|
## Server Health
|
||||||
|
|
||||||
|
@ -695,14 +695,14 @@ agent. The table below describes the additional metrics exported by the proxy.
|
||||||
**Requirements:**
|
**Requirements:**
|
||||||
- Consul 1.13.0+
|
- Consul 1.13.0+
|
||||||
|
|
||||||
[Cluster peering](/docs/connect/cluster-peering) refers to enabling communication between Consul clusters through a peer connection, as opposed to a federated connection. Consul collects metrics that describe the number of services exported to a peered cluster. Peering metrics are only emitted by the leader server.
|
[Cluster peering](/docs/connect/cluster-peering) refers to enabling communication between Consul clusters through a peer connection, as opposed to a federated connection. Consul collects metrics that describe the number of services exported to a peered cluster. Peering metrics are only emitted by the leader server.
|
||||||
|
|
||||||
| Metric | Description | Unit | Type |
|
| Metric | Description | Unit | Type |
|
||||||
| ------------------------------------- | ----------------------------------------------------------------------| ------ | ------- |
|
| ------------------------------------- | ----------------------------------------------------------------------| ------ | ------- |
|
||||||
| `consul.peering.exported_services` | Counts the number of services exported to a peer cluster. | count | gauge |
|
| `consul.peering.exported_services` | Counts the number of services exported to a peer cluster. | count | gauge |
|
||||||
|
|
||||||
### Labels
|
### Labels
|
||||||
Consul attaches the following labels to metric values.
|
Consul attaches the following labels to metric values.
|
||||||
| Label Name | Description | Possible values |
|
| Label Name | Description | Possible values |
|
||||||
| ------------------------------------- | ---------------------------------------------------------------------- | ------------------------------------------ |
|
| ------------------------------------- | ---------------------------------------------------------------------- | ------------------------------------------ |
|
||||||
| `peer_name` | The name of the peering on the reporting cluster or leader. | Any defined peer name in the cluster |
|
| `peer_name` | The name of the peering on the reporting cluster or leader. | Any defined peer name in the cluster |
|
||||||
|
|
|
@ -25,50 +25,82 @@ You must implement the following requirements to create and use cluster peering
|
||||||
- At least two Kubernetes clusters
|
- At least two Kubernetes clusters
|
||||||
- The installation must be running on Consul on Kubernetes version 0.47.1 or later
|
- The installation must be running on Consul on Kubernetes version 0.47.1 or later
|
||||||
|
|
||||||
### Helm chart configuration
|
### Prepare for install
|
||||||
|
|
||||||
To establish cluster peering through Kubernetes, deploy clusters with the following Helm values.
|
1. After provisioning a Kubernetes cluster and setting up your kubeconfig file to manage access to multiple Kubernetes clusters, export the Kubernetes context names for future use with `kubectl`. For more information on how to use kubeconfig and contexts, refer to [Configure access to multiple clusters](https://kubernetes.io/docs/tasks/access-application-cluster/configure-access-multiple-clusters/) on the Kubernetes documentation website.
|
||||||
|
|
||||||
<CodeBlockConfig filename="values.yaml">
|
You can use the following methods to get the context names for your clusters:
|
||||||
|
|
||||||
|
* Issue the `kubectl config current-context` command to get the context for the cluster you are currently in.
|
||||||
|
* Issue the `kubectl config get-contexts` command to get all configured contexts in your kubeconfig file.
|
||||||
|
|
||||||
|
```shell-session
|
||||||
|
$ export CLUSTER1_CONTEXT=<CONTEXT for first Kubernetes cluster>
|
||||||
|
$ export CLUSTER2_CONTEXT=<CONTEXT for second Kubernetes cluster>
|
||||||
|
```
|
||||||
|
|
||||||
```yaml
|
1. To establish cluster peering through Kubernetes, create a `values.yaml` file with the following Helm values.
|
||||||
global:
|
|
||||||
image: "hashicorp/consul:1.13.1"
|
With these values,
|
||||||
peering:
|
the servers in each cluster will be exposed over a Kubernetes Load balancer service. This service can be customized
|
||||||
|
using [`server.exposeService`](/docs/k8s/helm#v-server-exposeservice).
|
||||||
|
|
||||||
|
When generating a peering token from one of the clusters, Consul uses the address(es) of the load balancer in the peering token so that the peering stream goes through the load balancer in front of the servers. For customizing the addresses used in the peering token, refer to [`global.peering.tokenGeneration`](/docs/k8s/helm#v-global-peering-tokengeneration).
|
||||||
|
|
||||||
|
<CodeBlockConfig filename="values.yaml">
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
global:
|
||||||
|
image: "hashicorp/consul:1.13.1"
|
||||||
|
peering:
|
||||||
|
enabled: true
|
||||||
|
connectInject:
|
||||||
enabled: true
|
enabled: true
|
||||||
connectInject:
|
dns:
|
||||||
enabled: true
|
enabled: true
|
||||||
controller:
|
enableRedirection: true
|
||||||
enabled: true
|
server:
|
||||||
meshGateway:
|
exposeService:
|
||||||
enabled: true
|
enabeld: true
|
||||||
replicas: 1
|
controller:
|
||||||
```
|
enabled: true
|
||||||
|
meshGateway:
|
||||||
|
enabled: true
|
||||||
|
replicas: 1
|
||||||
|
```
|
||||||
|
|
||||||
</CodeBlockConfig>
|
</CodeBlockConfig>
|
||||||
|
|
||||||
|
### Install Consul on Kubernetes
|
||||||
|
|
||||||
Install Consul on Kubernetes on each Kubernetes cluster by applying `values.yaml` using the Helm CLI. With these values,
|
1. Install Consul on Kubernetes on each Kubernetes cluster by applying `values.yaml` using the Helm CLI.
|
||||||
the servers in each cluster will be exposed over a Kubernetes Load balancer service. This service can be customized
|
|
||||||
using [`server.exposeService`](/docs/k8s/helm#v-server-exposeservice). When generating a peering token from one of the
|
1. Install Consul on Kubernetes on `cluster-01`
|
||||||
clusters, the address(es) of the load balancer will be used in the peering token, so the peering stream will go through
|
|
||||||
the load balancer in front of the servers. For customizing the addresses used in the peering token, see
|
```shell-session
|
||||||
[`global.peering.tokenGeneration`](/docs/k8s/helm#v-global-peering-tokengeneration).
|
$ export HELM_RELEASE_NAME=cluster-01
|
||||||
|
```
|
||||||
|
|
||||||
```shell-session
|
```shell-session
|
||||||
$ export HELM_RELEASE_NAME=cluster-name
|
$ helm install ${HELM_RELEASE_NAME} hashicorp/consul --create-namespace --namespace consul --version "0.47.1" --values values.yaml --kube-context $CLUSTER1_CONTEXT
|
||||||
```
|
```
|
||||||
|
1. Install Consul on Kubernetes on `cluster-02`
|
||||||
|
|
||||||
|
```shell-session
|
||||||
|
$ export HELM_RELEASE_NAME=cluster-02
|
||||||
|
```
|
||||||
|
|
||||||
```shell-session
|
```shell-session
|
||||||
$ helm install ${HELM_RELEASE_NAME} hashicorp/consul --version "0.47.1" --values values.yaml
|
$ helm install ${HELM_RELEASE_NAME} hashicorp/consul --create-namespace --namespace consul --version "0.47.1" --values values.yaml --kube-context $CLUSTER2_CONTEXT
|
||||||
```
|
```
|
||||||
|
|
||||||
## Create a peering token
|
## Create a peering token
|
||||||
|
|
||||||
To peer Kubernetes clusters running Consul, you need to create a peering token and share it with the other cluster.
|
To peer Kubernetes clusters running Consul, you need to create a peering token and share it with the other cluster. As part of the peering process, the peer names for each respective cluster within the peering are established by using the `metadata.name` values for the `PeeringAcceptor` and `PeeringDialer` CRDs.
|
||||||
|
|
||||||
1. In `cluster-01`, create the `PeeringAcceptor` custom resource.
|
1. In `cluster-01`, create the `PeeringAcceptor` custom resource.
|
||||||
|
|
||||||
<CodeBlockConfig filename="acceptor.yml">
|
<CodeBlockConfig filename="acceptor.yaml">
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
apiVersion: consul.hashicorp.com/v1alpha1
|
apiVersion: consul.hashicorp.com/v1alpha1
|
||||||
|
@ -88,13 +120,13 @@ To peer Kubernetes clusters running Consul, you need to create a peering token a
|
||||||
1. Apply the `PeeringAcceptor` resource to the first cluster.
|
1. Apply the `PeeringAcceptor` resource to the first cluster.
|
||||||
|
|
||||||
```shell-session
|
```shell-session
|
||||||
$ kubectl apply --filename acceptor.yml
|
$ kubectl --context $CLUSTER1_CONTEXT apply --filename acceptor.yaml
|
||||||
````
|
````
|
||||||
|
|
||||||
1. Save your peering token so that you can export it to the other cluster.
|
1. Save your peering token so that you can export it to the other cluster.
|
||||||
|
|
||||||
```shell-session
|
```shell-session
|
||||||
$ kubectl get secret peering-token --output yaml > peering-token.yml
|
$ kubectl --context $CLUSTER1_CONTEXT get secret peering-token --output yaml > peering-token.yaml
|
||||||
```
|
```
|
||||||
|
|
||||||
## Establish a peering connection between clusters
|
## Establish a peering connection between clusters
|
||||||
|
@ -102,12 +134,12 @@ To peer Kubernetes clusters running Consul, you need to create a peering token a
|
||||||
1. Apply the peering token to the second cluster.
|
1. Apply the peering token to the second cluster.
|
||||||
|
|
||||||
```shell-session
|
```shell-session
|
||||||
$ kubectl apply --filename peering-token.yml
|
$ kubectl --context $CLUSTER2_CONTEXT apply --filename peering-token.yaml
|
||||||
```
|
```
|
||||||
|
|
||||||
1. In `cluster-02`, create the `PeeringDialer` custom resource.
|
1. In `cluster-02`, create the `PeeringDialer` custom resource.
|
||||||
|
|
||||||
<CodeBlockConfig filename="dialer.yml">
|
<CodeBlockConfig filename="dialer.yaml">
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
apiVersion: consul.hashicorp.com/v1alpha1
|
apiVersion: consul.hashicorp.com/v1alpha1
|
||||||
|
@ -127,27 +159,74 @@ To peer Kubernetes clusters running Consul, you need to create a peering token a
|
||||||
1. Apply the `PeeringDialer` resource to the second cluster.
|
1. Apply the `PeeringDialer` resource to the second cluster.
|
||||||
|
|
||||||
```shell-session
|
```shell-session
|
||||||
$ kubectl apply --filename dialer.yml
|
$ kubectl --context $CLUSTER2_CONTEXT apply --filename dialer.yaml
|
||||||
```
|
```
|
||||||
|
|
||||||
## Export services between clusters
|
## Export services between clusters
|
||||||
|
|
||||||
1. For the service in "cluster-02" that you want to export, add the following [annotation](/docs/k8s/annotations-and-labels) to your service's pods.
|
1. For the service in "cluster-02" that you want to export, add the following [annotation](/docs/k8s/annotations-and-labels) to your service's pods.
|
||||||
|
|
||||||
<CodeBlockConfig filename="backend-service.yml">
|
<CodeBlockConfig filename="backend-service.yaml">
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
##…
|
# Service to expose backend
|
||||||
annotations:
|
apiVersion: v1
|
||||||
"consul.hashicorp.com/connect-inject": "true"
|
kind: Service
|
||||||
##…
|
metadata:
|
||||||
|
name: backend-service
|
||||||
|
spec:
|
||||||
|
selector:
|
||||||
|
app: backend
|
||||||
|
ports:
|
||||||
|
- name: http
|
||||||
|
protocol: TCP
|
||||||
|
port: 80
|
||||||
|
targetPort: 9090
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: ServiceAccount
|
||||||
|
metadata:
|
||||||
|
name: backend
|
||||||
|
---
|
||||||
|
# deployment for backend
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: backend
|
||||||
|
labels:
|
||||||
|
app: backend
|
||||||
|
spec:
|
||||||
|
replicas: 1
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app: backend
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: backend
|
||||||
|
annotations:
|
||||||
|
"consul.hashicorp.com/connect-inject": "true"
|
||||||
|
spec:
|
||||||
|
serviceAccountName: backend
|
||||||
|
containers:
|
||||||
|
- name: backend
|
||||||
|
image: nicholasjackson/fake-service:v0.22.4
|
||||||
|
ports:
|
||||||
|
- containerPort: 9090
|
||||||
|
env:
|
||||||
|
- name: "LISTEN_ADDR"
|
||||||
|
value: "0.0.0.0:9090"
|
||||||
|
- name: "NAME"
|
||||||
|
value: "backend"
|
||||||
|
- name: "MESSAGE"
|
||||||
|
value: "Response from backend"
|
||||||
```
|
```
|
||||||
|
|
||||||
</CodeBlockConfig>
|
</CodeBlockConfig>
|
||||||
|
|
||||||
1. In `cluster-02`, create an `ExportedServices` custom resource.
|
1. In `cluster-02`, create an `ExportedServices` custom resource.
|
||||||
|
|
||||||
<CodeBlockConfig filename="exportedsvc.yml">
|
<CodeBlockConfig filename="exportedsvc.yaml">
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
apiVersion: consul.hashicorp.com/v1alpha1
|
apiVersion: consul.hashicorp.com/v1alpha1
|
||||||
|
@ -166,7 +245,7 @@ To peer Kubernetes clusters running Consul, you need to create a peering token a
|
||||||
1. Apply the service file and the `ExportedServices` resource for the second cluster.
|
1. Apply the service file and the `ExportedServices` resource for the second cluster.
|
||||||
|
|
||||||
```shell-session
|
```shell-session
|
||||||
$ kubectl apply --filename backend-service.yml --filename exportedsvc.yml
|
$ kubectl apply --context $CLUSTER2_CONTEXT --filename backend-service.yaml --filename exportedsvc.yaml
|
||||||
```
|
```
|
||||||
|
|
||||||
## Authorize services for peers
|
## Authorize services for peers
|
||||||
|
@ -195,18 +274,71 @@ To peer Kubernetes clusters running Consul, you need to create a peering token a
|
||||||
1. Apply the intentions to the second cluster.
|
1. Apply the intentions to the second cluster.
|
||||||
|
|
||||||
```shell-session
|
```shell-session
|
||||||
$ kubectl apply --filename intention.yml
|
$ kubectl --context $CLUSTER2_CONTEXT apply --filename intention.yml
|
||||||
```
|
```
|
||||||
|
|
||||||
1. For the services in `cluster-01` that you want to access the "backend-service," add the following annotations to the service file.
|
1. For the services in `cluster-01` that you want to access the "backend-service," add the following annotations to the service file. To dial the upstream service from an application, ensure that the requests are sent to the correct DNS name as specified in [Service Virtual IP Lookups](/docs/discovery/dns#service-virtual-ip-lookups).
|
||||||
|
|
||||||
<CodeBlockConfig filename="frontend-service.yml">
|
<CodeBlockConfig filename="frontend-service.yaml">
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
##…
|
# Service to expose frontend
|
||||||
annotations:
|
apiVersion: v1
|
||||||
"consul.hashicorp.com/connect-inject": "true"
|
kind: Service
|
||||||
##…
|
metadata:
|
||||||
|
name: frontend-service
|
||||||
|
spec:
|
||||||
|
selector:
|
||||||
|
app: frontend
|
||||||
|
ports:
|
||||||
|
- name: http
|
||||||
|
protocol: TCP
|
||||||
|
port: 9090
|
||||||
|
targetPort: 9090
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: ServiceAccount
|
||||||
|
metadata:
|
||||||
|
name: frontend
|
||||||
|
---
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: frontend
|
||||||
|
labels:
|
||||||
|
app: frontend
|
||||||
|
spec:
|
||||||
|
replicas: 1
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app: frontend
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: frontend
|
||||||
|
annotations:
|
||||||
|
"consul.hashicorp.com/connect-inject": "true"
|
||||||
|
spec:
|
||||||
|
serviceAccountName: frontend
|
||||||
|
containers:
|
||||||
|
- name: frontend
|
||||||
|
image: nicholasjackson/fake-service:v0.22.4
|
||||||
|
securityContext:
|
||||||
|
capabilities:
|
||||||
|
add: ["NET_ADMIN"]
|
||||||
|
ports:
|
||||||
|
- containerPort: 9090
|
||||||
|
env:
|
||||||
|
- name: "LISTEN_ADDR"
|
||||||
|
value: "0.0.0.0:9090"
|
||||||
|
- name: "UPSTREAM_URIS"
|
||||||
|
value: "http://backend-service.virtual.cluster-02.consul"
|
||||||
|
- name: "NAME"
|
||||||
|
value: "frontend"
|
||||||
|
- name: "MESSAGE"
|
||||||
|
value: "Hello World"
|
||||||
|
- name: "HTTP_CLIENT_KEEP_ALIVES"
|
||||||
|
value: "false"
|
||||||
```
|
```
|
||||||
|
|
||||||
</CodeBlockConfig>
|
</CodeBlockConfig>
|
||||||
|
@ -214,18 +346,45 @@ To peer Kubernetes clusters running Consul, you need to create a peering token a
|
||||||
1. Apply the service file to the first cluster.
|
1. Apply the service file to the first cluster.
|
||||||
|
|
||||||
```shell-session
|
```shell-session
|
||||||
$ kubectl apply --filename frontend-service.yml
|
$ kubectl --context $CLUSTER1_CONTEXT apply --filename frontend-service.yaml
|
||||||
```
|
```
|
||||||
|
|
||||||
1. Run the following command in `frontend-service` and check the output to confirm that you peered your clusters successfully.
|
1. Run the following command in `frontend-service` and check the output to confirm that you peered your clusters successfully.
|
||||||
|
|
||||||
```shell-session
|
```shell-session
|
||||||
$ kubectl exec -it $(kubectl get pod -l app=frontend -o name) -- curl localhost:1234
|
$ kubectl --context $CLUSTER1_CONTEXT exec -it $(kubectl --context $CLUSTER1_CONTEXT get pod -l app=frontend -o name) -- curl localhost:9090
|
||||||
{
|
{
|
||||||
"name": "backend-service",
|
"name": "frontend",
|
||||||
##…
|
"uri": "/",
|
||||||
"body": "Response from backend",
|
"type": "HTTP",
|
||||||
"code": 200
|
"ip_addresses": [
|
||||||
|
"10.16.2.11"
|
||||||
|
],
|
||||||
|
"start_time": "2022-08-26T23:40:01.167199",
|
||||||
|
"end_time": "2022-08-26T23:40:01.226951",
|
||||||
|
"duration": "59.752279ms",
|
||||||
|
"body": "Hello World",
|
||||||
|
"upstream_calls": {
|
||||||
|
"http://backend-service.virtual.cluster-02.consul": {
|
||||||
|
"name": "backend",
|
||||||
|
"uri": "http://backend-service.virtual.cluster-02.consul",
|
||||||
|
"type": "HTTP",
|
||||||
|
"ip_addresses": [
|
||||||
|
"10.32.2.10"
|
||||||
|
],
|
||||||
|
"start_time": "2022-08-26T23:40:01.223503",
|
||||||
|
"end_time": "2022-08-26T23:40:01.224653",
|
||||||
|
"duration": "1.149666ms",
|
||||||
|
"headers": {
|
||||||
|
"Content-Length": "266",
|
||||||
|
"Content-Type": "text/plain; charset=utf-8",
|
||||||
|
"Date": "Fri, 26 Aug 2022 23:40:01 GMT"
|
||||||
|
},
|
||||||
|
"body": "Response from backend",
|
||||||
|
"code": 200
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"code": 200
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
@ -17,7 +17,7 @@ description: >-
|
||||||
|
|
||||||
With Consul Enterprise v1.8.0+, audit logging can be used to capture a clear and
|
With Consul Enterprise v1.8.0+, audit logging can be used to capture a clear and
|
||||||
actionable log of authenticated events (both attempted and committed) that Consul
|
actionable log of authenticated events (both attempted and committed) that Consul
|
||||||
processes via its HTTP API. These events are compiled them into a JSON format for easy export
|
processes via its HTTP API. These events are then compiled into a JSON format for easy export
|
||||||
and contain a timestamp, the operation performed, and the user who initiated the action.
|
and contain a timestamp, the operation performed, and the user who initiated the action.
|
||||||
|
|
||||||
Audit logging enables security and compliance teams within an organization to get
|
Audit logging enables security and compliance teams within an organization to get
|
||||||
|
|
|
@ -20,8 +20,7 @@ description: >-
|
||||||
## Supported Software
|
## Supported Software
|
||||||
|
|
||||||
- Consul 1.11.x, Consul 1.12.x and Consul 1.13.1+
|
- Consul 1.11.x, Consul 1.12.x and Consul 1.13.1+
|
||||||
- Kubernetes 1.19+
|
- Kubernetes 1.19-1.23
|
||||||
- Kubernetes 1.24 is not supported at this time.
|
|
||||||
- Kubectl 1.21+
|
- Kubectl 1.21+
|
||||||
- Envoy proxy support is determined by the Consul version deployed. Refer to
|
- Envoy proxy support is determined by the Consul version deployed. Refer to
|
||||||
[Envoy Integration](/docs/connect/proxies/envoy) for details.
|
[Envoy Integration](/docs/connect/proxies/envoy) for details.
|
||||||
|
|
Loading…
Reference in New Issue