Add Upstream Service Targeting to Property Override Extension (#17517)

* add upstream service targeting to property override extension

* Also add baseline goldens for service specific property override extension.
* Refactor the extension framework to put more logic into the templates.

* fix up the golden tests
This commit is contained in:
Eric Haberkorn 2023-05-30 14:53:42 -04:00 committed by GitHub
parent 44f90132e0
commit d99312b86e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
36 changed files with 2634 additions and 247 deletions

View File

@ -72,16 +72,19 @@ func (a *awsLambda) CanApply(config *extensioncommon.RuntimeConfig) bool {
// PatchRoute modifies the routing configuration for a service of kind TerminatingGateway. If the kind is
// not TerminatingGateway, then it can not be modified.
func (a *awsLambda) PatchRoute(r *extensioncommon.RuntimeConfig, route *envoy_route_v3.RouteConfiguration) (*envoy_route_v3.RouteConfiguration, bool, error) {
if r.Kind != api.ServiceKindTerminatingGateway {
return route, false, nil
func (a *awsLambda) PatchRoute(p extensioncommon.RoutePayload) (*envoy_route_v3.RouteConfiguration, bool, error) {
cfg := p.RuntimeConfig
if cfg.Kind != api.ServiceKindTerminatingGateway {
return p.Message, false, nil
}
// Only patch outbound routes.
if extensioncommon.IsRouteToLocalAppCluster(route) {
return route, false, nil
if p.IsInbound() {
return p.Message, false, nil
}
route := p.Message
for _, virtualHost := range route.VirtualHosts {
for _, route := range virtualHost.Routes {
action, ok := route.Action.(*envoy_route_v3.Route_Route)
@ -101,16 +104,18 @@ func (a *awsLambda) PatchRoute(r *extensioncommon.RuntimeConfig, route *envoy_ro
}
// PatchCluster patches the provided envoy cluster with data required to support an AWS lambda function
func (a *awsLambda) PatchCluster(_ *extensioncommon.RuntimeConfig, c *envoy_cluster_v3.Cluster) (*envoy_cluster_v3.Cluster, bool, error) {
func (a *awsLambda) PatchCluster(p extensioncommon.ClusterPayload) (*envoy_cluster_v3.Cluster, bool, error) {
// Only patch outbound clusters.
if extensioncommon.IsLocalAppCluster(c) {
return c, false, nil
if p.IsInbound() {
return p.Message, false, nil
}
transportSocket, err := extensioncommon.MakeUpstreamTLSTransportSocket(&envoy_tls_v3.UpstreamTlsContext{
Sni: "*.amazonaws.com",
})
c := p.Message
if err != nil {
return c, false, fmt.Errorf("failed to make transport socket: %w", err)
}
@ -168,9 +173,11 @@ func (a *awsLambda) PatchCluster(_ *extensioncommon.RuntimeConfig, c *envoy_clus
// PatchFilter patches the provided envoy filter with an inserted lambda filter being careful not to
// overwrite the http filters.
func (a *awsLambda) PatchFilter(_ *extensioncommon.RuntimeConfig, filter *envoy_listener_v3.Filter, isInboundListener bool) (*envoy_listener_v3.Filter, bool, error) {
func (a *awsLambda) PatchFilter(p extensioncommon.FilterPayload) (*envoy_listener_v3.Filter, bool, error) {
filter := p.Message
// Only patch outbound filters.
if isInboundListener {
if p.IsInbound() {
return filter, false, nil
}

View File

@ -202,7 +202,10 @@ func TestPatchCluster(t *testing.T) {
// Test patching the cluster
rc := extensioncommon.RuntimeConfig{}
patchedCluster, patchSuccess, err := tc.lambda.PatchCluster(&rc, tc.input)
patchedCluster, patchSuccess, err := tc.lambda.PatchCluster(extensioncommon.ClusterPayload{
RuntimeConfig: &rc,
Message: tc.input,
})
if tc.isErrExpected {
assert.Error(t, err)
assert.False(t, patchSuccess)
@ -307,7 +310,10 @@ func TestPatchRoute(t *testing.T) {
for name, tc := range tests {
t.Run(name, func(t *testing.T) {
l := awsLambda{}
r, ok, err := l.PatchRoute(tc.conf, tc.route)
r, ok, err := l.PatchRoute(extensioncommon.RoutePayload{
RuntimeConfig: tc.conf,
Message: tc.route,
})
require.NoError(t, err)
require.Equal(t, tc.expectRoute, r)
require.Equal(t, tc.expectBool, ok)
@ -456,7 +462,14 @@ func TestPatchFilter(t *testing.T) {
PayloadPassthrough: true,
InvocationMode: "asynchronous",
}
f, ok, err := l.PatchFilter(nil, tc.filter, tc.isInboundFilter)
d := extensioncommon.TrafficDirectionOutbound
if tc.isInboundFilter {
d = extensioncommon.TrafficDirectionInbound
}
f, ok, err := l.PatchFilter(extensioncommon.FilterPayload{
Message: tc.filter,
TrafficDirection: d,
})
require.Equal(t, tc.expectBool, ok)
if tc.expectErr == "" {
require.NoError(t, err)

View File

@ -601,7 +601,7 @@ func (t Target) clusterName(cfg *cmn.RuntimeConfig) (string, error) {
for service, upstream := range cfg.Upstreams {
if service == t.Service {
for sni := range upstream.SNI {
for sni := range upstream.SNIs {
return sni, nil
}
}

View File

@ -102,10 +102,11 @@ func (p *ratelimit) CanApply(config *extensioncommon.RuntimeConfig) bool {
// PatchFilter inserts a http local rate_limit filter at the head of
// envoy.filters.network.http_connection_manager filters
func (p ratelimit) PatchFilter(_ *extensioncommon.RuntimeConfig, filter *envoy_listener_v3.Filter, isInboundListener bool) (*envoy_listener_v3.Filter, bool, error) {
func (r ratelimit) PatchFilter(p extensioncommon.FilterPayload) (*envoy_listener_v3.Filter, bool, error) {
filter := p.Message
// rate limit is only applied to the inbound listener of the service itself
// since the limit is aggregated from all downstream connections.
if !isInboundListener {
if !p.IsInbound() {
return filter, false, nil
}
@ -123,34 +124,34 @@ func (p ratelimit) PatchFilter(_ *extensioncommon.RuntimeConfig, filter *envoy_l
tokenBucket := envoy_type_v3.TokenBucket{}
if p.TokensPerFill != nil {
if r.TokensPerFill != nil {
tokenBucket.TokensPerFill = &wrappers.UInt32Value{
Value: uint32(*p.TokensPerFill),
Value: uint32(*r.TokensPerFill),
}
}
if p.MaxTokens != nil {
tokenBucket.MaxTokens = uint32(*p.MaxTokens)
if r.MaxTokens != nil {
tokenBucket.MaxTokens = uint32(*r.MaxTokens)
}
if p.FillInterval != nil {
tokenBucket.FillInterval = durationpb.New(time.Duration(*p.FillInterval) * time.Second)
if r.FillInterval != nil {
tokenBucket.FillInterval = durationpb.New(time.Duration(*r.FillInterval) * time.Second)
}
var FilterEnabledDefault *envoy_core_v3.RuntimeFractionalPercent
if p.FilterEnabled != nil {
if r.FilterEnabled != nil {
FilterEnabledDefault = &envoy_core_v3.RuntimeFractionalPercent{
DefaultValue: &envoy_type_v3.FractionalPercent{
Numerator: *p.FilterEnabled,
Numerator: *r.FilterEnabled,
Denominator: envoy_type_v3.FractionalPercent_HUNDRED,
},
}
}
var FilterEnforcedDefault *envoy_core_v3.RuntimeFractionalPercent
if p.FilterEnforced != nil {
if r.FilterEnforced != nil {
FilterEnforcedDefault = &envoy_core_v3.RuntimeFractionalPercent{
DefaultValue: &envoy_type_v3.FractionalPercent{
Numerator: *p.FilterEnforced,
Numerator: *r.FilterEnforced,
Denominator: envoy_type_v3.FractionalPercent_HUNDRED,
},
}

View File

@ -67,14 +67,16 @@ func (l *lua) CanApply(config *extensioncommon.RuntimeConfig) bool {
return string(config.Kind) == l.ProxyType
}
func (l *lua) matchesListenerDirection(isInboundListener bool) bool {
func (l *lua) matchesListenerDirection(p extensioncommon.FilterPayload) bool {
isInboundListener := p.IsInbound()
return (!isInboundListener && l.Listener == "outbound") || (isInboundListener && l.Listener == "inbound")
}
// PatchFilter inserts a lua filter directly prior to envoy.filters.http.router.
func (l *lua) PatchFilter(_ *extensioncommon.RuntimeConfig, filter *envoy_listener_v3.Filter, isInboundListener bool) (*envoy_listener_v3.Filter, bool, error) {
func (l *lua) PatchFilter(p extensioncommon.FilterPayload) (*envoy_listener_v3.Filter, bool, error) {
filter := p.Message
// Make sure filter matches extension config.
if !l.matchesListenerDirection(isInboundListener) {
if !l.matchesListenerDirection(p) {
return filter, false, nil
}

View File

@ -2,7 +2,6 @@ package propertyoverride
import (
"fmt"
"strings"
envoy_cluster_v3 "github.com/envoyproxy/go-control-plane/envoy/config/cluster/v3"
envoy_endpoint_v3 "github.com/envoyproxy/go-control-plane/envoy/config/endpoint/v3"
@ -14,14 +13,10 @@ import (
"github.com/hashicorp/consul/api"
"github.com/hashicorp/consul/envoyextensions/extensioncommon"
"github.com/hashicorp/consul/lib/maps"
)
type stringSet map[string]struct{}
type propertyOverride struct {
extensioncommon.BasicExtensionAdapter
// Patches are an array of Patch operations to be applied to the target resource(s).
Patches []Patch
// Debug controls error messages when Path matching fails.
@ -43,7 +38,42 @@ type ResourceFilter struct {
// TrafficDirection determines whether the patch will be applied to a service's inbound
// or outbound resources.
// This field is required.
TrafficDirection TrafficDirection
TrafficDirection extensioncommon.TrafficDirection
// Services indicates which upstream services will have corresponding Envoy resources patched.
// This includes directly targeted and discovery chain services. If Services is omitted or
// empty, all resources matching the filter will be targeted (including TProxy, which
// implicitly corresponds to any number of upstreams). Services must be omitted unless
// TrafficDirection is set to outbound.
Services []*ServiceName
}
func matchesResourceFilter[K proto.Message](rf ResourceFilter, resourceType ResourceType, payload extensioncommon.Payload[K]) bool {
if resourceType != rf.ResourceType {
return false
}
if payload.TrafficDirection != rf.TrafficDirection {
return false
}
if len(rf.Services) == 0 {
return true
}
for _, s := range rf.Services {
if payload.ServiceName == nil || s.CompoundServiceName != *payload.ServiceName {
continue
}
return true
}
return false
}
type ServiceName struct {
api.CompoundServiceName
}
// ResourceType is the type of Envoy resource being patched.
@ -56,23 +86,13 @@ const (
ResourceTypeRoute ResourceType = "route"
)
var ResourceTypes = stringSet{
var ResourceTypes = extensioncommon.StringSet{
string(ResourceTypeCluster): {},
string(ResourceTypeClusterLoadAssignment): {},
string(ResourceTypeListener): {},
string(ResourceTypeRoute): {},
string(ResourceTypeListener): {},
}
// TrafficDirection determines whether inbound or outbound Envoy resources will be patched.
type TrafficDirection string
const (
TrafficDirectionInbound TrafficDirection = "inbound"
TrafficDirectionOutbound TrafficDirection = "outbound"
)
var TrafficDirections = stringSet{string(TrafficDirectionInbound): {}, string(TrafficDirectionOutbound): {}}
// Op is the type of JSON Patch operation being applied.
type Op string
@ -81,10 +101,10 @@ const (
OpRemove Op = "remove"
)
var Ops = stringSet{string(OpAdd): {}, string(OpRemove): {}}
var Ops = extensioncommon.StringSet{string(OpAdd): {}, string(OpRemove): {}}
// validProxyTypes is the set of supported proxy types for this extension.
var validProxyTypes = stringSet{
var validProxyTypes = extensioncommon.StringSet{
string(api.ServiceKindConnectProxy): struct{}{},
string(api.ServiceKindTerminatingGateway): struct{}{},
}
@ -139,27 +159,58 @@ type Patch struct {
var _ extensioncommon.BasicExtension = (*propertyOverride)(nil)
func (c *stringSet) checkRequired(v, fieldName string) error {
if _, ok := (*c)[v]; !ok {
if v == "" {
return fmt.Errorf("field %s is required", fieldName)
}
return fmt.Errorf("invalid %s '%q'; supported values: %s",
fieldName, v, strings.Join(maps.SliceOfKeys(*c), ", "))
func (f *ResourceFilter) isEmpty() bool {
if f == nil {
return true
}
return nil
if len(f.Services) > 0 {
return false
}
if string(f.TrafficDirection) != "" {
return false
}
if string(f.ResourceType) != "" {
return false
}
return true
}
func (f *ResourceFilter) validate() error {
if f == nil || *f == (ResourceFilter{}) {
if f == nil || f.isEmpty() {
return fmt.Errorf("field ResourceFilter is required")
}
if err := ResourceTypes.checkRequired(string(f.ResourceType), "ResourceType"); err != nil {
if err := ResourceTypes.CheckRequired(string(f.ResourceType), "ResourceType"); err != nil {
return err
}
if err := TrafficDirections.checkRequired(string(f.TrafficDirection), "TrafficDirection"); err != nil {
if err := extensioncommon.TrafficDirections.CheckRequired(string(f.TrafficDirection), "TrafficDirection"); err != nil {
return err
}
for i := range f.Services {
sn := f.Services[i]
sn.normalize()
if err := sn.validate(); err != nil {
return err
}
}
return nil
}
func (sn *ServiceName) normalize() {
extensioncommon.NormalizeServiceName(&sn.CompoundServiceName)
}
func (sn *ServiceName) validate() error {
if sn.Name == "" {
return fmt.Errorf("service name is required")
}
return nil
}
@ -169,7 +220,7 @@ func (p *Patch) validate(debug bool) error {
return err
}
if err := Ops.checkRequired(string(p.Op), "Op"); err != nil {
if err := Ops.CheckRequired(string(p.Op), "Op"); err != nil {
return err
}
@ -209,7 +260,7 @@ func (p *propertyOverride) validate() error {
}
}
if err := validProxyTypes.checkRequired(string(p.ProxyType), "ProxyType"); err != nil {
if err := validProxyTypes.CheckRequired(string(p.ProxyType), "ProxyType"); err != nil {
resultErr = multierror.Append(resultErr, err)
}
@ -243,57 +294,35 @@ func (p *propertyOverride) CanApply(config *extensioncommon.RuntimeConfig) bool
}
// PatchRoute patches the provided Envoy Route with any applicable `route` ResourceType patches.
func (p *propertyOverride) PatchRoute(_ *extensioncommon.RuntimeConfig, r *envoy_route_v3.RouteConfiguration) (*envoy_route_v3.RouteConfiguration, bool, error) {
d := TrafficDirectionOutbound
if extensioncommon.IsRouteToLocalAppCluster(r) {
d = TrafficDirectionInbound
}
return patchResourceType[*envoy_route_v3.RouteConfiguration](r, p, ResourceTypeRoute, d, &defaultStructPatcher[*envoy_route_v3.RouteConfiguration]{})
func (p *propertyOverride) PatchRoute(payload extensioncommon.RoutePayload) (*envoy_route_v3.RouteConfiguration, bool, error) {
return patchResourceType[*envoy_route_v3.RouteConfiguration](p, ResourceTypeRoute, payload, &defaultStructPatcher[*envoy_route_v3.RouteConfiguration]{})
}
// PatchCluster patches the provided Envoy Cluster with any applicable `cluster` ResourceType patches.
func (p *propertyOverride) PatchCluster(_ *extensioncommon.RuntimeConfig, c *envoy_cluster_v3.Cluster) (*envoy_cluster_v3.Cluster, bool, error) {
d := TrafficDirectionOutbound
if extensioncommon.IsLocalAppCluster(c) {
d = TrafficDirectionInbound
}
return patchResourceType[*envoy_cluster_v3.Cluster](c, p, ResourceTypeCluster, d, &defaultStructPatcher[*envoy_cluster_v3.Cluster]{})
func (p *propertyOverride) PatchCluster(payload extensioncommon.ClusterPayload) (*envoy_cluster_v3.Cluster, bool, error) {
return patchResourceType[*envoy_cluster_v3.Cluster](p, ResourceTypeCluster, payload, &defaultStructPatcher[*envoy_cluster_v3.Cluster]{})
}
// PatchClusterLoadAssignment patches the provided Envoy ClusterLoadAssignment with any applicable `cluster-load-assignment` ResourceType patches.
func (p *propertyOverride) PatchClusterLoadAssignment(_ *extensioncommon.RuntimeConfig, c *envoy_endpoint_v3.ClusterLoadAssignment) (*envoy_endpoint_v3.ClusterLoadAssignment, bool, error) {
d := TrafficDirectionOutbound
if extensioncommon.IsLocalAppClusterLoadAssignment(c) {
d = TrafficDirectionInbound
}
return patchResourceType[*envoy_endpoint_v3.ClusterLoadAssignment](c, p, ResourceTypeClusterLoadAssignment, d, &defaultStructPatcher[*envoy_endpoint_v3.ClusterLoadAssignment]{})
func (p *propertyOverride) PatchClusterLoadAssignment(payload extensioncommon.ClusterLoadAssignmentPayload) (*envoy_endpoint_v3.ClusterLoadAssignment, bool, error) {
return patchResourceType[*envoy_endpoint_v3.ClusterLoadAssignment](p, ResourceTypeClusterLoadAssignment, payload, &defaultStructPatcher[*envoy_endpoint_v3.ClusterLoadAssignment]{})
}
// PatchListener patches the provided Envoy Listener with any applicable `listener` ResourceType patches.
func (p *propertyOverride) PatchListener(_ *extensioncommon.RuntimeConfig, l *envoy_listener_v3.Listener) (*envoy_listener_v3.Listener, bool, error) {
d := TrafficDirectionOutbound
if extensioncommon.IsInboundPublicListener(l) {
d = TrafficDirectionInbound
}
return patchResourceType[*envoy_listener_v3.Listener](l, p, ResourceTypeListener, d, &defaultStructPatcher[*envoy_listener_v3.Listener]{})
}
// PatchFilter does nothing as this extension does not target Filters directly.
func (p *propertyOverride) PatchFilter(_ *extensioncommon.RuntimeConfig, f *envoy_listener_v3.Filter, _ bool) (*envoy_listener_v3.Filter, bool, error) {
return f, false, nil
func (p *propertyOverride) PatchListener(payload extensioncommon.ListenerPayload) (*envoy_listener_v3.Listener, bool, error) {
return patchResourceType[*envoy_listener_v3.Listener](p, ResourceTypeListener, payload, &defaultStructPatcher[*envoy_listener_v3.Listener]{})
}
// patchResourceType applies Patches matching the given ResourceType to the target K.
// This helper simplifies implementation of the above per-type patch methods defined by BasicExtension.
func patchResourceType[K proto.Message](k K, p *propertyOverride, t ResourceType, d TrafficDirection, patcher structPatcher[K]) (K, bool, error) {
func patchResourceType[K proto.Message](p *propertyOverride, resourceType ResourceType, payload extensioncommon.Payload[K], patcher structPatcher[K]) (K, bool, error) {
resultPatched := false
var resultErr error
k := payload.Message
for _, patch := range p.Patches {
if patch.ResourceFilter.ResourceType != t {
continue
}
if patch.ResourceFilter.TrafficDirection != d {
if !matchesResourceFilter(patch.ResourceFilter, resourceType, payload) {
continue
}
newK, err := patcher.applyPatch(k, patch, p.Debug)

View File

@ -2,11 +2,13 @@ package propertyoverride
import (
"fmt"
routev3 "github.com/envoyproxy/go-control-plane/envoy/config/route/v3"
"strings"
"testing"
clusterv3 "github.com/envoyproxy/go-control-plane/envoy/config/cluster/v3"
endpointv3 "github.com/envoyproxy/go-control-plane/envoy/config/endpoint/v3"
listenerv3 "github.com/envoyproxy/go-control-plane/envoy/config/listener/v3"
routev3 "github.com/envoyproxy/go-control-plane/envoy/config/route/v3"
"github.com/hashicorp/consul/api"
"github.com/hashicorp/consul/envoyextensions/extensioncommon"
"github.com/stretchr/testify/require"
@ -32,7 +34,7 @@ func TestConstructor(t *testing.T) {
makeResourceFilter := func(overrides map[string]any) map[string]any {
f := map[string]any{
"ResourceType": ResourceTypeRoute,
"TrafficDirection": TrafficDirectionOutbound,
"TrafficDirection": extensioncommon.TrafficDirectionOutbound,
}
return applyOverrides(f, overrides)
}
@ -63,7 +65,7 @@ func TestConstructor(t *testing.T) {
errMsg string
}
validTestCase := func(o Op, d TrafficDirection, t ResourceType) testCase {
validTestCase := func(o Op, d extensioncommon.TrafficDirection, t ResourceType) testCase {
var v any = "foo"
if o != OpAdd {
v = nil
@ -214,6 +216,19 @@ func TestConstructor(t *testing.T) {
ok: false,
errMsg: fmt.Sprintf("field Value is not supported for %s operation", OpRemove),
},
"empty service name": {
arguments: makeArguments(map[string]any{"Patches": []map[string]any{
makePatch(map[string]any{
"ResourceFilter": makeResourceFilter(map[string]any{
"Services": []ServiceName{
{CompoundServiceName: api.CompoundServiceName{}},
},
}),
}),
}}),
ok: false,
errMsg: "service name is required",
},
// See decode.HookWeakDecodeFromSlice for more details. In practice, we can end up
// with a "Patches" field decoded to the single "Patch" value contained in the
// serialized slice (raised from the containing slice). Using WeakDecode solves
@ -222,7 +237,7 @@ func TestConstructor(t *testing.T) {
// by WeakDecode as it is a more-permissive version of the default behavior.
"single value Patches decoded as map construction succeeds": {
arguments: makeArguments(map[string]any{"Patches": makePatch(map[string]any{})}),
expected: validTestCase(OpAdd, TrafficDirectionOutbound, ResourceTypeRoute).expected,
expected: validTestCase(OpAdd, extensioncommon.TrafficDirectionOutbound, ResourceTypeRoute).expected,
ok: true,
},
"invalid ProxyType": {
@ -248,10 +263,10 @@ func TestConstructor(t *testing.T) {
}
for o := range Ops {
for d := range TrafficDirections {
for d := range extensioncommon.TrafficDirections {
for t := range ResourceTypes {
cases["valid everything: "+strings.Join([]string{o, d, t}, ",")] =
validTestCase(Op(o), TrafficDirection(d), ResourceType(t))
validTestCase(Op(o), extensioncommon.TrafficDirection(d), ResourceType(t))
}
}
}
@ -294,32 +309,62 @@ func Test_patchResourceType(t *testing.T) {
Patches: patches,
}
}
makePatchWithPath := func(t ResourceType, d TrafficDirection, p string) Patch {
makePatchWithPath := func(filter ResourceFilter, p string) Patch {
return Patch{
ResourceFilter: ResourceFilter{
ResourceType: t,
TrafficDirection: d,
},
Op: OpAdd,
Path: p,
Value: 1,
ResourceFilter: filter,
Op: OpAdd,
Path: p,
Value: 1,
}
}
makePatch := func(t ResourceType, d TrafficDirection) Patch {
return makePatchWithPath(t, d, "/foo")
makePatch := func(filter ResourceFilter) Patch {
return makePatchWithPath(filter, "/foo")
}
clusterOutbound := makePatch(ResourceTypeCluster, TrafficDirectionOutbound)
clusterInbound := makePatch(ResourceTypeCluster, TrafficDirectionInbound)
routeOutbound := makePatch(ResourceTypeRoute, TrafficDirectionOutbound)
routeOutbound2 := makePatchWithPath(ResourceTypeRoute, TrafficDirectionOutbound, "/bar")
routeInbound := makePatch(ResourceTypeRoute, TrafficDirectionInbound)
svc1 := ServiceName{
CompoundServiceName: api.CompoundServiceName{Name: "svc1"},
}
svc2 := ServiceName{
CompoundServiceName: api.CompoundServiceName{Name: "svc2"},
}
clusterOutbound := makePatch(ResourceFilter{
ResourceType: ResourceTypeCluster,
TrafficDirection: extensioncommon.TrafficDirectionOutbound,
})
clusterInbound := makePatch(ResourceFilter{
ResourceType: ResourceTypeCluster,
TrafficDirection: extensioncommon.TrafficDirectionInbound,
})
listenerOutbound := makePatch(ResourceFilter{
ResourceType: ResourceTypeListener,
TrafficDirection: extensioncommon.TrafficDirectionOutbound,
})
listenerOutbound2 := makePatchWithPath(ResourceFilter{
ResourceType: ResourceTypeListener,
TrafficDirection: extensioncommon.TrafficDirectionOutbound,
}, "/bar")
listenerInbound := makePatch(ResourceFilter{
ResourceType: ResourceTypeListener,
TrafficDirection: extensioncommon.TrafficDirectionInbound,
})
routeOutbound := makePatch(ResourceFilter{
ResourceType: ResourceTypeRoute,
TrafficDirection: extensioncommon.TrafficDirectionOutbound,
})
routeOutbound2 := makePatchWithPath(ResourceFilter{
ResourceType: ResourceTypeRoute,
TrafficDirection: extensioncommon.TrafficDirectionOutbound,
}, "/bar")
routeInbound := makePatch(ResourceFilter{
ResourceType: ResourceTypeRoute,
TrafficDirection: extensioncommon.TrafficDirectionInbound,
})
type args struct {
d TrafficDirection
k proto.Message
p *propertyOverride
t ResourceType
resourceType ResourceType
payload extensioncommon.Payload[proto.Message]
p *propertyOverride
}
type testCase struct {
args args
@ -329,79 +374,179 @@ func Test_patchResourceType(t *testing.T) {
cases := map[string]testCase{
"outbound gets matching patch": {
args: args{
d: TrafficDirectionOutbound,
k: &clusterv3.Cluster{},
resourceType: ResourceTypeCluster,
payload: extensioncommon.Payload[proto.Message]{
TrafficDirection: extensioncommon.TrafficDirectionOutbound,
Message: &clusterv3.Cluster{},
},
p: makeExtension(clusterOutbound),
t: ResourceTypeCluster,
},
expectPatched: true,
wantApplied: []Patch{clusterOutbound},
},
"inbound gets matching patch": {
args: args{
d: TrafficDirectionInbound,
k: &clusterv3.Cluster{},
resourceType: ResourceTypeCluster,
payload: extensioncommon.Payload[proto.Message]{
TrafficDirection: extensioncommon.TrafficDirectionInbound,
Message: &clusterv3.Cluster{},
},
p: makeExtension(clusterInbound),
t: ResourceTypeCluster,
},
expectPatched: true,
wantApplied: []Patch{clusterInbound},
},
"multiple resources same direction only gets matching resource": {
args: args{
d: TrafficDirectionOutbound,
k: &clusterv3.Cluster{},
p: makeExtension(clusterOutbound, routeOutbound),
t: ResourceTypeCluster,
resourceType: ResourceTypeCluster,
payload: extensioncommon.Payload[proto.Message]{
TrafficDirection: extensioncommon.TrafficDirectionOutbound,
Message: &clusterv3.Cluster{},
},
p: makeExtension(clusterOutbound, listenerOutbound),
},
expectPatched: true,
wantApplied: []Patch{clusterOutbound},
},
"multiple directions same resource only gets matching direction": {
args: args{
d: TrafficDirectionOutbound,
k: &clusterv3.Cluster{},
resourceType: ResourceTypeCluster,
payload: extensioncommon.Payload[proto.Message]{
TrafficDirection: extensioncommon.TrafficDirectionOutbound,
Message: &clusterv3.Cluster{},
},
p: makeExtension(clusterOutbound, clusterInbound),
t: ResourceTypeCluster,
},
expectPatched: true,
wantApplied: []Patch{clusterOutbound},
},
"multiple directions and resources only gets matching patch": {
args: args{
d: TrafficDirectionInbound,
k: &routev3.RouteConfiguration{},
p: makeExtension(clusterOutbound, clusterInbound, routeOutbound, routeInbound),
t: ResourceTypeRoute,
resourceType: ResourceTypeRoute,
payload: extensioncommon.Payload[proto.Message]{
TrafficDirection: extensioncommon.TrafficDirectionInbound,
Message: &routev3.RouteConfiguration{},
},
p: makeExtension(clusterOutbound, clusterInbound, listenerOutbound, listenerInbound, routeOutbound, routeOutbound2, routeInbound),
},
expectPatched: true,
wantApplied: []Patch{routeInbound},
},
"multiple directions and resources multiple matches gets all matching patches": {
args: args{
d: TrafficDirectionOutbound,
k: &routev3.RouteConfiguration{},
p: makeExtension(clusterOutbound, clusterInbound, routeOutbound, routeInbound, routeOutbound2),
t: ResourceTypeRoute,
resourceType: ResourceTypeRoute,
payload: extensioncommon.Payload[proto.Message]{
TrafficDirection: extensioncommon.TrafficDirectionOutbound,
Message: &routev3.RouteConfiguration{},
},
p: makeExtension(clusterOutbound, clusterInbound, listenerOutbound, listenerInbound, listenerOutbound2, routeOutbound, routeOutbound2, routeInbound),
},
expectPatched: true,
wantApplied: []Patch{routeOutbound, routeOutbound2},
},
"multiple directions and resources no matches gets no patches": {
args: args{
d: TrafficDirectionOutbound,
k: &routev3.RouteConfiguration{},
p: makeExtension(clusterInbound, routeOutbound, routeInbound, routeOutbound2),
t: ResourceTypeCluster,
resourceType: ResourceTypeCluster,
payload: extensioncommon.Payload[proto.Message]{
TrafficDirection: extensioncommon.TrafficDirectionOutbound,
Message: &clusterv3.Cluster{},
},
p: makeExtension(clusterInbound, listenerOutbound, listenerInbound, listenerOutbound2, routeInbound, routeOutbound),
},
expectPatched: false,
wantApplied: nil,
},
}
type resourceTypeServiceMatch struct {
resourceType ResourceType
message proto.Message
}
resourceTypeCases := []resourceTypeServiceMatch{
{
resourceType: ResourceTypeCluster,
message: &clusterv3.Cluster{},
},
{
resourceType: ResourceTypeListener,
message: &listenerv3.Listener{},
},
{
resourceType: ResourceTypeRoute,
message: &routev3.RouteConfiguration{},
},
{
resourceType: ResourceTypeClusterLoadAssignment,
message: &endpointv3.ClusterLoadAssignment{},
},
}
for _, tc := range resourceTypeCases {
{
patch := makePatch(ResourceFilter{
ResourceType: tc.resourceType,
TrafficDirection: extensioncommon.TrafficDirectionOutbound,
Services: []*ServiceName{
{CompoundServiceName: svc2.CompoundServiceName},
},
})
cases[fmt.Sprintf("%s - no match", tc.resourceType)] = testCase{
args: args{
resourceType: tc.resourceType,
payload: extensioncommon.Payload[proto.Message]{
TrafficDirection: extensioncommon.TrafficDirectionOutbound,
ServiceName: &svc1.CompoundServiceName,
Message: tc.message,
RuntimeConfig: &extensioncommon.RuntimeConfig{
Upstreams: map[api.CompoundServiceName]*extensioncommon.UpstreamData{
svc1.CompoundServiceName: {},
},
},
},
p: makeExtension(patch),
},
expectPatched: false,
wantApplied: nil,
}
}
{
patch := makePatch(ResourceFilter{
ResourceType: tc.resourceType,
TrafficDirection: extensioncommon.TrafficDirectionOutbound,
Services: []*ServiceName{
{CompoundServiceName: svc2.CompoundServiceName},
{CompoundServiceName: svc1.CompoundServiceName},
},
})
cases[fmt.Sprintf("%s - match", tc.resourceType)] = testCase{
args: args{
resourceType: tc.resourceType,
payload: extensioncommon.Payload[proto.Message]{
TrafficDirection: extensioncommon.TrafficDirectionOutbound,
ServiceName: &svc1.CompoundServiceName,
Message: tc.message,
RuntimeConfig: &extensioncommon.RuntimeConfig{
Upstreams: map[api.CompoundServiceName]*extensioncommon.UpstreamData{
svc1.CompoundServiceName: {},
},
},
},
p: makeExtension(patch),
},
expectPatched: true,
wantApplied: []Patch{patch},
}
}
}
for n, tc := range cases {
t.Run(n, func(t *testing.T) {
mockPatcher := MockPatcher[proto.Message]{}
_, patched, err := patchResourceType[proto.Message](tc.args.k, tc.args.p, tc.args.t, tc.args.d, &mockPatcher)
_, patched, err := patchResourceType[proto.Message](tc.args.p, tc.args.resourceType, tc.args.payload, &mockPatcher)
require.NoError(t, err, "unexpected error from mock")
require.Equal(t, tc.expectPatched, patched)

View File

@ -195,7 +195,7 @@ func (p *pluginConfig) asyncDataSource(rtCfg *extensioncommon.RuntimeConfig) (*e
clusterSNI := ""
for service, upstream := range rtCfg.Upstreams {
if service == remote.HttpURI.Service {
for sni := range upstream.SNI {
for sni := range upstream.SNIs {
clusterSNI = sni
break
}

View File

@ -609,7 +609,7 @@ func makeTestRuntimeConfig(protocol string, enterprise bool) *extensioncommon.Ru
Namespace: acl.NamespaceOrDefault(ns),
Partition: acl.PartitionOrDefault(ap),
}: {
SNI: map[string]struct{}{"test-file-server": {}},
SNIs: map[string]struct{}{"test-file-server": {}},
EnvoyID: "test-file-server",
},
},

View File

@ -34,7 +34,6 @@ import (
const (
meshGatewayExportedClusterNamePrefix = "exported~"
failoverClusterNamePrefix = "failover-target~"
)
// clustersFromSnapshot returns the xDS API representation of the "clusters" in the snapshot.
@ -1915,7 +1914,7 @@ func (s *ResourceGenerator) getTargetClusterName(upstreamsSnapshot *proxycfg.Con
}
clusterName = CustomizeClusterName(clusterName, chain)
if failover {
clusterName = failoverClusterNamePrefix + clusterName
clusterName = xdscommon.FailoverClusterNamePrefix + clusterName
}
if forMeshGateway {
clusterName = meshGatewayExportedClusterNamePrefix + clusterName

View File

@ -26,6 +26,7 @@ import (
"github.com/hashicorp/consul/agent/structs"
"github.com/hashicorp/consul/agent/xds/extensionruntime"
"github.com/hashicorp/consul/api"
"github.com/hashicorp/consul/envoyextensions/extensioncommon"
"github.com/hashicorp/consul/envoyextensions/xdscommon"
"github.com/hashicorp/consul/sdk/testutil"
)
@ -110,7 +111,7 @@ end`,
{
"ResourceFilter": map[string]interface{}{
"ResourceType": propertyoverride.ResourceTypeCluster,
"TrafficDirection": propertyoverride.TrafficDirectionOutbound,
"TrafficDirection": extensioncommon.TrafficDirectionOutbound,
},
"Op": "add",
"Path": "/outlier_detection/success_rate_minimum_hosts",
@ -125,7 +126,7 @@ end`,
{
"ResourceFilter": map[string]interface{}{
"ResourceType": propertyoverride.ResourceTypeCluster,
"TrafficDirection": propertyoverride.TrafficDirectionOutbound,
"TrafficDirection": extensioncommon.TrafficDirectionOutbound,
},
"Op": "add",
"Path": "/outlier_detection",
@ -143,7 +144,7 @@ end`,
{
"ResourceFilter": map[string]interface{}{
"ResourceType": propertyoverride.ResourceTypeCluster,
"TrafficDirection": propertyoverride.TrafficDirectionOutbound,
"TrafficDirection": extensioncommon.TrafficDirectionOutbound,
},
"Op": "remove",
"Path": "/outlier_detection",
@ -157,7 +158,7 @@ end`,
{
"ResourceFilter": map[string]interface{}{
"ResourceType": propertyoverride.ResourceTypeCluster,
"TrafficDirection": propertyoverride.TrafficDirectionOutbound,
"TrafficDirection": extensioncommon.TrafficDirectionOutbound,
},
"Op": "add",
"Path": "/upstream_connection_options/tcp_keepalive/keepalive_probes",
@ -172,7 +173,7 @@ end`,
{
"ResourceFilter": map[string]interface{}{
"ResourceType": propertyoverride.ResourceTypeCluster,
"TrafficDirection": propertyoverride.TrafficDirectionOutbound,
"TrafficDirection": extensioncommon.TrafficDirectionOutbound,
},
"Op": "add",
"Path": "/round_robin_lb_config",
@ -181,6 +182,169 @@ end`,
},
})
propertyOverrideServiceDefaultsClusterLoadAssignmentOutboundAdd := makePropOverrideNsFunc(
map[string]interface{}{
"Patches": []map[string]interface{}{
{
"ResourceFilter": map[string]interface{}{
"ResourceType": propertyoverride.ResourceTypeClusterLoadAssignment,
"TrafficDirection": extensioncommon.TrafficDirectionOutbound,
},
"Op": "add",
"Path": "/policy/overprovisioning_factor",
"Value": 123,
},
},
})
propertyOverrideServiceDefaultsClusterLoadAssignmentInboundAdd := makePropOverrideNsFunc(
map[string]interface{}{
"Patches": []map[string]interface{}{
{
"ResourceFilter": map[string]interface{}{
"ResourceType": propertyoverride.ResourceTypeClusterLoadAssignment,
"TrafficDirection": extensioncommon.TrafficDirectionInbound,
},
"Op": "add",
"Path": "/policy/overprovisioning_factor",
"Value": 123,
},
},
})
propertyOverrideServiceDefaultsListenerInboundAdd := makePropOverrideNsFunc(
map[string]interface{}{
"Patches": []map[string]interface{}{
{
"ResourceFilter": map[string]interface{}{
"ResourceType": propertyoverride.ResourceTypeListener,
"TrafficDirection": extensioncommon.TrafficDirectionInbound,
},
"Op": "add",
"Path": "/stat_prefix",
"Value": "custom.stats",
},
},
})
propertyOverrideServiceDefaultsListenerOutboundAdd := makePropOverrideNsFunc(
map[string]interface{}{
"Patches": []map[string]interface{}{
{
"ResourceFilter": map[string]interface{}{
"ResourceType": propertyoverride.ResourceTypeListener,
"TrafficDirection": extensioncommon.TrafficDirectionOutbound,
},
"Op": "add",
"Path": "/stat_prefix",
"Value": "custom.stats",
},
},
})
propertyOverrideServiceDefaultsListenerOutboundDoesntApplyToInbound := makePropOverrideNsFunc(
map[string]interface{}{
"Patches": []map[string]interface{}{
{
"ResourceFilter": map[string]interface{}{
"ResourceType": propertyoverride.ResourceTypeListener,
"TrafficDirection": extensioncommon.TrafficDirectionInbound,
},
"Op": "add",
"Path": "/stat_prefix",
"Value": "custom.stats.inbound.only",
},
{
"ResourceFilter": map[string]interface{}{
"ResourceType": propertyoverride.ResourceTypeListener,
"TrafficDirection": extensioncommon.TrafficDirectionOutbound,
},
"Op": "add",
"Path": "/stat_prefix",
"Value": "custom.stats.outbound.only",
},
},
})
// Reverse order of above patches, to prove order is inconsequential
propertyOverrideServiceDefaultsListenerInboundDoesntApplyToOutbound := makePropOverrideNsFunc(
map[string]interface{}{
"Patches": []map[string]interface{}{
{
"ResourceFilter": map[string]interface{}{
"ResourceType": propertyoverride.ResourceTypeListener,
"TrafficDirection": extensioncommon.TrafficDirectionOutbound,
},
"Op": "add",
"Path": "/stat_prefix",
"Value": "custom.stats.outbound.only",
},
{
"ResourceFilter": map[string]interface{}{
"ResourceType": propertyoverride.ResourceTypeListener,
"TrafficDirection": extensioncommon.TrafficDirectionInbound,
},
"Op": "add",
"Path": "/stat_prefix",
"Value": "custom.stats.inbound.only",
},
},
})
propertyOverridePatchSpecificUpstreamService := makePropOverrideNsFunc(
map[string]interface{}{
"Patches": []map[string]interface{}{
{
"ResourceFilter": map[string]interface{}{
"ResourceType": propertyoverride.ResourceTypeListener,
"TrafficDirection": extensioncommon.TrafficDirectionOutbound,
"Services": []propertyoverride.ServiceName{
{CompoundServiceName: api.CompoundServiceName{Name: "db"}},
},
},
"Op": "add",
"Path": "/stat_prefix",
"Value": "custom.stats.outbound.only",
},
{
"ResourceFilter": map[string]interface{}{
"ResourceType": propertyoverride.ResourceTypeRoute,
"TrafficDirection": extensioncommon.TrafficDirectionOutbound,
"Services": []propertyoverride.ServiceName{
{CompoundServiceName: api.CompoundServiceName{Name: "db"}},
},
},
"Op": "add",
"Path": "/most_specific_header_mutations_wins",
"Value": true,
},
{
"ResourceFilter": map[string]interface{}{
"ResourceType": propertyoverride.ResourceTypeCluster,
"TrafficDirection": extensioncommon.TrafficDirectionOutbound,
"Services": []propertyoverride.ServiceName{
{CompoundServiceName: api.CompoundServiceName{Name: "db"}},
},
},
"Op": "add",
"Path": "/outlier_detection/success_rate_minimum_hosts",
"Value": 1234,
},
{
"ResourceFilter": map[string]interface{}{
"ResourceType": propertyoverride.ResourceTypeClusterLoadAssignment,
"TrafficDirection": extensioncommon.TrafficDirectionOutbound,
"Services": []propertyoverride.ServiceName{
{CompoundServiceName: api.CompoundServiceName{Name: "db"}},
},
},
"Op": "add",
"Path": "/policy/overprovisioning_factor",
"Value": 1234,
},
},
})
tests := []struct {
name string
create func(t testinf.T) *proxycfg.ConfigSnapshot
@ -215,6 +379,54 @@ end`,
return proxycfg.TestConfigSnapshotDiscoveryChain(t, "default", false, propertyOverrideServiceDefaultsRemoveOutlierDetection, nil)
},
},
{
name: "propertyoverride-cluster-load-assignment-outbound-add",
create: func(t testinf.T) *proxycfg.ConfigSnapshot {
return proxycfg.TestConfigSnapshotDiscoveryChain(t, "default", false, propertyOverrideServiceDefaultsClusterLoadAssignmentOutboundAdd, nil)
},
},
{
name: "propertyoverride-cluster-load-assignment-inbound-add",
create: func(t testinf.T) *proxycfg.ConfigSnapshot {
return proxycfg.TestConfigSnapshotDiscoveryChain(t, "default", false, propertyOverrideServiceDefaultsClusterLoadAssignmentInboundAdd, nil)
},
},
{
name: "propertyoverride-listener-outbound-add",
create: func(t testinf.T) *proxycfg.ConfigSnapshot {
return proxycfg.TestConfigSnapshotDiscoveryChain(t, "default", false, propertyOverrideServiceDefaultsListenerOutboundAdd, nil)
},
},
{
name: "propertyoverride-listener-inbound-add",
create: func(t testinf.T) *proxycfg.ConfigSnapshot {
return proxycfg.TestConfigSnapshotDiscoveryChain(t, "default", false, propertyOverrideServiceDefaultsListenerInboundAdd, nil)
},
},
{
name: "propertyoverride-outbound-doesnt-apply-to-inbound",
create: func(t testinf.T) *proxycfg.ConfigSnapshot {
return proxycfg.TestConfigSnapshotDiscoveryChain(t, "default", false, propertyOverrideServiceDefaultsListenerOutboundDoesntApplyToInbound, nil)
},
},
{
name: "propertyoverride-inbound-doesnt-apply-to-outbound",
create: func(t testinf.T) *proxycfg.ConfigSnapshot {
return proxycfg.TestConfigSnapshotDiscoveryChain(t, "default", false, propertyOverrideServiceDefaultsListenerInboundDoesntApplyToOutbound, nil)
},
},
{
name: "propertyoverride-patch-specific-upstream-service-splitter",
create: func(t testinf.T) *proxycfg.ConfigSnapshot {
return proxycfg.TestConfigSnapshotDiscoveryChain(t, "splitter-with-resolver-redirect-multidc", false, propertyOverridePatchSpecificUpstreamService, nil)
},
},
{
name: "propertyoverride-patch-specific-upstream-service-failover",
create: func(t testinf.T) *proxycfg.ConfigSnapshot {
return proxycfg.TestConfigSnapshotDiscoveryChain(t, "failover", false, propertyOverridePatchSpecificUpstreamService, nil)
},
},
{
name: "lambda-connect-proxy",
create: func(t testinf.T) *proxycfg.ConfigSnapshot {

View File

@ -56,19 +56,27 @@ func GetRuntimeConfigurations(cfgSnap *proxycfg.ConfigSnapshot) map[api.Compound
// TODO(peering): consider PeerUpstreamEndpoints in addition to DiscoveryChain
// These are the discovery chains for upstreams which have the Envoy Extensions applied to the local service.
for uid, dc := range cfgSnap.ConnectProxy.DiscoveryChain {
for uid, chain := range cfgSnap.ConnectProxy.DiscoveryChain {
compoundServiceName := upstreamIDToCompoundServiceName(uid)
extensionsMap[compoundServiceName] = convertEnvoyExtensions(dc.EnvoyExtensions)
extensionsMap[compoundServiceName] = convertEnvoyExtensions(chain.EnvoyExtensions)
primarySNI := connect.ServiceSNI(uid.Name, "", chain.Namespace, chain.Partition, cfgSnap.Datacenter, trustDomain)
snis := make(map[string]struct{})
for _, t := range chain.Targets {
// SNI isn't set for peered services. We don't support peered services yet.
if t.SNI != "" {
snis[t.SNI] = struct{}{}
}
}
meta := uid.EnterpriseMeta
sni := connect.ServiceSNI(uid.Name, "", meta.NamespaceOrDefault(), meta.PartitionOrDefault(), cfgSnap.Datacenter, trustDomain)
outgoingKind, ok := outgoingKindByService[compoundServiceName]
if !ok {
outgoingKind = api.ServiceKindConnectProxy
}
upstreamMap[compoundServiceName] = &extensioncommon.UpstreamData{
SNI: map[string]struct{}{sni: {}},
PrimarySNI: primarySNI,
SNIs: snis,
VIP: vipForService[compoundServiceName],
EnvoyID: uid.EnvoyID(),
OutgoingProxyKind: outgoingKind,
@ -101,10 +109,10 @@ func GetRuntimeConfigurations(cfgSnap *proxycfg.ConfigSnapshot) map[api.Compound
compoundServiceName := serviceNameToCompoundServiceName(svc)
extensionsMap[compoundServiceName] = convertEnvoyExtensions(c.EnvoyExtensions)
sni := connect.ServiceSNI(svc.Name, "", svc.NamespaceOrDefault(), svc.PartitionOrDefault(), cfgSnap.Datacenter, trustDomain)
primarySNI := connect.ServiceSNI(svc.Name, "", svc.NamespaceOrDefault(), svc.PartitionOrDefault(), cfgSnap.Datacenter, trustDomain)
envoyID := proxycfg.NewUpstreamIDFromServiceName(svc)
snis := map[string]struct{}{sni: {}}
snis := map[string]struct{}{primarySNI: {}}
resolver, hasResolver := cfgSnap.TerminatingGateway.ServiceResolvers[svc]
if hasResolver {
@ -115,7 +123,8 @@ func GetRuntimeConfigurations(cfgSnap *proxycfg.ConfigSnapshot) map[api.Compound
}
upstreamMap[compoundServiceName] = &extensioncommon.UpstreamData{
SNI: snis,
PrimarySNI: primarySNI,
SNIs: snis,
EnvoyID: envoyID.EnvoyID(),
OutgoingProxyKind: api.ServiceKindTerminatingGateway,
}

View File

@ -58,28 +58,32 @@ func TestGetRuntimeConfigurations_TerminatingGateway(t *testing.T) {
IsSourcedFromUpstream: true,
Upstreams: map[api.CompoundServiceName]*extensioncommon.UpstreamData{
apiService: {
SNI: map[string]struct{}{
PrimarySNI: "api.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",
SNIs: map[string]struct{}{
"api.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul": {},
},
EnvoyID: "api",
OutgoingProxyKind: "terminating-gateway",
},
cacheService: {
SNI: map[string]struct{}{
PrimarySNI: "cache.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",
SNIs: map[string]struct{}{
"cache.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul": {},
},
EnvoyID: "cache",
OutgoingProxyKind: "terminating-gateway",
},
dbService: {
SNI: map[string]struct{}{
PrimarySNI: "db.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",
SNIs: map[string]struct{}{
"db.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul": {},
},
EnvoyID: "db",
OutgoingProxyKind: "terminating-gateway",
},
webService: {
SNI: map[string]struct{}{
PrimarySNI: "web.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",
SNIs: map[string]struct{}{
"canary1.web.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul": {},
"canary2.web.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul": {},
"web.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul": {},
@ -134,10 +138,25 @@ func TestGetRuntimeConfigurations_ConnectProxy(t *testing.T) {
EnvoyExtensions: envoyExtensions,
}
serviceDefaultsV2 := &structs.ServiceConfigEntry{
Kind: structs.ServiceDefaults,
Name: "db-v2",
Protocol: "http",
}
serviceSplitter := &structs.ServiceSplitterConfigEntry{
Kind: structs.ServiceSplitter,
Name: "db",
Splits: []structs.ServiceSplit{
{Weight: 50},
{Weight: 50, Service: "db-v2"},
},
}
// Setup a snapshot where the db upstream is on a connect proxy.
snapConnect := proxycfg.TestConfigSnapshotDiscoveryChain(t, "default", false, nil, nil, serviceDefaults)
snapConnect := proxycfg.TestConfigSnapshotDiscoveryChain(t, "default", false, nil, nil, serviceDefaults, serviceDefaultsV2, serviceSplitter)
// Setup a snapshot where the db upstream is on a terminating gateway.
snapTermGw := proxycfg.TestConfigSnapshotDiscoveryChain(t, "register-to-terminating-gateway", false, nil, nil, serviceDefaults)
snapTermGw := proxycfg.TestConfigSnapshotDiscoveryChain(t, "register-to-terminating-gateway", false, nil, nil, serviceDefaults, serviceDefaultsV2, serviceSplitter)
// Setup a snapshot with the local service web has extensions.
snapWebConnect := proxycfg.TestConfigSnapshotDiscoveryChain(t, "default", false, func(ns *structs.NodeService) {
ns.Proxy.EnvoyExtensions = envoyExtensions
@ -164,8 +183,10 @@ func TestGetRuntimeConfigurations_ConnectProxy(t *testing.T) {
IsSourcedFromUpstream: true,
Upstreams: map[api.CompoundServiceName]*extensioncommon.UpstreamData{
dbService: {
SNI: map[string]struct{}{
"db.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul": {},
PrimarySNI: "db.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",
SNIs: map[string]struct{}{
"db.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul": {},
"db-v2.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul": {},
},
EnvoyID: "db",
OutgoingProxyKind: "connect-proxy",
@ -193,8 +214,10 @@ func TestGetRuntimeConfigurations_ConnectProxy(t *testing.T) {
IsSourcedFromUpstream: true,
Upstreams: map[api.CompoundServiceName]*extensioncommon.UpstreamData{
dbService: {
SNI: map[string]struct{}{
"db.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul": {},
PrimarySNI: "db.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",
SNIs: map[string]struct{}{
"db.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul": {},
"db-v2.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul": {},
},
EnvoyID: "db",
OutgoingProxyKind: "terminating-gateway",
@ -224,7 +247,8 @@ func TestGetRuntimeConfigurations_ConnectProxy(t *testing.T) {
IsSourcedFromUpstream: false,
Upstreams: map[api.CompoundServiceName]*extensioncommon.UpstreamData{
dbService: {
SNI: map[string]struct{}{
PrimarySNI: "db.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",
SNIs: map[string]struct{}{
"db.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul": {},
},
EnvoyID: "db",
@ -245,7 +269,8 @@ func TestGetRuntimeConfigurations_ConnectProxy(t *testing.T) {
IsSourcedFromUpstream: false,
Upstreams: map[api.CompoundServiceName]*extensioncommon.UpstreamData{
dbService: {
SNI: map[string]struct{}{
PrimarySNI: "db.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",
SNIs: map[string]struct{}{
"db.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul": {},
},
EnvoyID: "db",

View File

@ -11,6 +11,7 @@ import (
"github.com/hashicorp/consul/agent/connect"
"github.com/hashicorp/consul/agent/proxycfg"
"github.com/hashicorp/consul/agent/structs"
"github.com/hashicorp/consul/envoyextensions/xdscommon"
)
type discoChainTargets struct {
@ -149,7 +150,7 @@ func (ft discoChainTargets) sequential() ([]discoChainTargetGroup, error) {
var targetGroups []discoChainTargetGroup
for i, t := range ft.targets {
targetGroups = append(targetGroups, discoChainTargetGroup{
ClusterName: fmt.Sprintf("%s%d~%s", failoverClusterNamePrefix, i, ft.baseClusterName),
ClusterName: fmt.Sprintf("%s%d~%s", xdscommon.FailoverClusterNamePrefix, i, ft.baseClusterName),
Targets: []targetInfo{t},
})
}

View File

@ -0,0 +1,199 @@
{
"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~0~db.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",
"failover-target~1~db.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul"
]
}
},
"connectTimeout": "33s",
"lbPolicy": "CLUSTER_PROVIDED",
"outlierDetection": {
"successRateMinimumHosts": 1234
}
},
{
"@type": "type.googleapis.com/envoy.config.cluster.v3.Cluster",
"name": "failover-target~0~db.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",
"altStatName": "failover-target~0~db.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",
"type": "EDS",
"edsClusterConfig": {
"edsConfig": {
"ads": {},
"resourceApiVersion": "V3"
}
},
"connectTimeout": "33s",
"circuitBreakers": {},
"outlierDetection": {
"successRateMinimumHosts": 1234
},
"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": "failover-target~1~db.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",
"altStatName": "failover-target~1~db.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",
"type": "EDS",
"edsClusterConfig": {
"edsConfig": {
"ads": {},
"resourceApiVersion": "V3"
}
},
"connectTimeout": "33s",
"circuitBreakers": {},
"outlierDetection": {
"successRateMinimumHosts": 1234
},
"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/fail"
}
]
}
},
"sni": "fail.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"
}

View File

@ -0,0 +1,179 @@
{
"versionInfo": "00000001",
"resources": [
{
"@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
}
}
}
}
]
}
]
}
},
{
"@type": "type.googleapis.com/envoy.config.cluster.v3.Cluster",
"name": "v1.db.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",
"altStatName": "v1.db.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",
"type": "EDS",
"edsClusterConfig": {
"edsConfig": {
"ads": {},
"resourceApiVersion": "V3"
}
},
"connectTimeout": "5s",
"circuitBreakers": {},
"outlierDetection": {
"successRateMinimumHosts": 1234
},
"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": "v1.db.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul"
}
}
},
{
"@type": "type.googleapis.com/envoy.config.cluster.v3.Cluster",
"name": "v2.db.default.dc2.internal.11111111-2222-3333-4444-555555555555.consul",
"altStatName": "v2.db.default.dc2.internal.11111111-2222-3333-4444-555555555555.consul",
"type": "EDS",
"edsClusterConfig": {
"edsConfig": {
"ads": {},
"resourceApiVersion": "V3"
}
},
"connectTimeout": "5s",
"circuitBreakers": {},
"outlierDetection": {
"successRateMinimumHosts": 1234
},
"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/dc2/svc/db"
}
]
}
},
"sni": "v2.db.default.dc2.internal.11111111-2222-3333-4444-555555555555.consul"
}
}
}
],
"typeUrl": "type.googleapis.com/envoy.config.cluster.v3.Cluster",
"nonce": "00000001"
}

View File

@ -0,0 +1,192 @@
{
"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~0~db.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",
"failover-target~1~db.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul"
]
}
},
"connectTimeout": "33s",
"lbPolicy": "CLUSTER_PROVIDED"
},
{
"@type": "type.googleapis.com/envoy.config.cluster.v3.Cluster",
"name": "failover-target~0~db.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",
"altStatName": "failover-target~0~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": "failover-target~1~db.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",
"altStatName": "failover-target~1~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/fail"
}
]
}
},
"sni": "fail.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"
}

View File

@ -0,0 +1,115 @@
{
"versionInfo": "00000001",
"resources": [
{
"@type": "type.googleapis.com/envoy.config.endpoint.v3.ClusterLoadAssignment",
"clusterName": "failover-target~0~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
}
]
}
],
"policy": {
"overprovisioningFactor": 1234
}
},
{
"@type": "type.googleapis.com/envoy.config.endpoint.v3.ClusterLoadAssignment",
"clusterName": "failover-target~1~db.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",
"endpoints": [
{
"lbEndpoints": [
{
"endpoint": {
"address": {
"socketAddress": {
"address": "10.20.1.1",
"portValue": 8080
}
}
},
"healthStatus": "HEALTHY",
"loadBalancingWeight": 1
},
{
"endpoint": {
"address": {
"socketAddress": {
"address": "10.20.1.2",
"portValue": 8080
}
}
},
"healthStatus": "HEALTHY",
"loadBalancingWeight": 1
}
]
}
],
"policy": {
"overprovisioningFactor": 1234
}
},
{
"@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"
}

View File

@ -0,0 +1,115 @@
{
"versionInfo": "00000001",
"resources": [
{
"@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
}
]
}
]
},
{
"@type": "type.googleapis.com/envoy.config.endpoint.v3.ClusterLoadAssignment",
"clusterName": "v1.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
}
]
}
],
"policy": {
"overprovisioningFactor": 1234
}
},
{
"@type": "type.googleapis.com/envoy.config.endpoint.v3.ClusterLoadAssignment",
"clusterName": "v2.db.default.dc2.internal.11111111-2222-3333-4444-555555555555.consul",
"endpoints": [
{
"lbEndpoints": [
{
"endpoint": {
"address": {
"socketAddress": {
"address": "10.20.1.1",
"portValue": 8080
}
}
},
"healthStatus": "HEALTHY",
"loadBalancingWeight": 1
},
{
"endpoint": {
"address": {
"socketAddress": {
"address": "10.20.1.2",
"portValue": 8080
}
}
},
"healthStatus": "HEALTHY",
"loadBalancingWeight": 1
}
]
}
],
"policy": {
"overprovisioningFactor": 1234
}
}
],
"typeUrl": "type.googleapis.com/envoy.config.endpoint.v3.ClusterLoadAssignment",
"nonce": "00000001"
}

View File

@ -0,0 +1,109 @@
{
"versionInfo": "00000001",
"resources": [
{
"@type": "type.googleapis.com/envoy.config.endpoint.v3.ClusterLoadAssignment",
"clusterName": "failover-target~0~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": "failover-target~1~db.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",
"endpoints": [
{
"lbEndpoints": [
{
"endpoint": {
"address": {
"socketAddress": {
"address": "10.20.1.1",
"portValue": 8080
}
}
},
"healthStatus": "HEALTHY",
"loadBalancingWeight": 1
},
{
"endpoint": {
"address": {
"socketAddress": {
"address": "10.20.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"
}

View File

@ -0,0 +1,235 @@
{
"versionInfo": "00000001",
"resources": [
{
"@type": "type.googleapis.com/envoy.config.listener.v3.Listener",
"name": "db:127.0.0.1:9191",
"address": {
"socketAddress": {
"address": "127.0.0.1",
"portValue": 9191
}
},
"statPrefix": "custom.stats.outbound.only",
"filterChains": [
{
"filters": [
{
"name": "envoy.filters.network.tcp_proxy",
"typedConfig": {
"@type": "type.googleapis.com/envoy.extensions.filters.network.tcp_proxy.v3.TcpProxy",
"statPrefix": "upstream.db.default.default.dc1",
"cluster": "db.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul"
}
}
]
}
],
"trafficDirection": "OUTBOUND"
},
{
"@type": "type.googleapis.com/envoy.config.listener.v3.Listener",
"name": "prepared_query:geo-cache:127.10.10.10:8181",
"address": {
"socketAddress": {
"address": "127.10.10.10",
"portValue": 8181
}
},
"filterChains": [
{
"filters": [
{
"name": "envoy.filters.network.tcp_proxy",
"typedConfig": {
"@type": "type.googleapis.com/envoy.extensions.filters.network.tcp_proxy.v3.TcpProxy",
"statPrefix": "upstream.prepared_query_geo-cache",
"cluster": "geo-cache.default.dc1.query.11111111-2222-3333-4444-555555555555.consul"
}
}
]
}
],
"trafficDirection": "OUTBOUND"
},
{
"@type": "type.googleapis.com/envoy.config.listener.v3.Listener",
"name": "public_listener:0.0.0.0:9999",
"address": {
"socketAddress": {
"address": "0.0.0.0",
"portValue": 9999
}
},
"filterChains": [
{
"filters": [
{
"name": "envoy.filters.network.http_connection_manager",
"typedConfig": {
"@type": "type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager",
"statPrefix": "public_listener",
"routeConfig": {
"name": "public_listener",
"virtualHosts": [
{
"name": "public_listener",
"domains": [
"*"
],
"routes": [
{
"match": {
"prefix": "/"
},
"route": {
"cluster": "local_app"
}
}
]
}
]
},
"httpFilters": [
{
"name": "envoy.filters.http.rbac",
"typedConfig": {
"@type": "type.googleapis.com/envoy.extensions.filters.http.rbac.v3.RBAC",
"rules": {}
}
},
{
"name": "envoy.filters.http.header_to_metadata",
"typedConfig": {
"@type": "type.googleapis.com/envoy.extensions.filters.http.header_to_metadata.v3.Config",
"requestRules": [
{
"header": "x-forwarded-client-cert",
"onHeaderPresent": {
"metadataNamespace": "consul",
"key": "trust-domain",
"regexValueRewrite": {
"pattern": {
"googleRe2": {},
"regex": ".*URI=spiffe://([^/]+.[^/]+)(?:/ap/([^/]+))?/ns/([^/]+)/dc/([^/]+)/svc/([^/;,]+).*"
},
"substitution": "\\1"
}
}
},
{
"header": "x-forwarded-client-cert",
"onHeaderPresent": {
"metadataNamespace": "consul",
"key": "partition",
"regexValueRewrite": {
"pattern": {
"googleRe2": {},
"regex": ".*URI=spiffe://([^/]+.[^/]+)(?:/ap/([^/]+))?/ns/([^/]+)/dc/([^/]+)/svc/([^/;,]+).*"
},
"substitution": "\\2"
}
}
},
{
"header": "x-forwarded-client-cert",
"onHeaderPresent": {
"metadataNamespace": "consul",
"key": "namespace",
"regexValueRewrite": {
"pattern": {
"googleRe2": {},
"regex": ".*URI=spiffe://([^/]+.[^/]+)(?:/ap/([^/]+))?/ns/([^/]+)/dc/([^/]+)/svc/([^/;,]+).*"
},
"substitution": "\\3"
}
}
},
{
"header": "x-forwarded-client-cert",
"onHeaderPresent": {
"metadataNamespace": "consul",
"key": "datacenter",
"regexValueRewrite": {
"pattern": {
"googleRe2": {},
"regex": ".*URI=spiffe://([^/]+.[^/]+)(?:/ap/([^/]+))?/ns/([^/]+)/dc/([^/]+)/svc/([^/;,]+).*"
},
"substitution": "\\4"
}
}
},
{
"header": "x-forwarded-client-cert",
"onHeaderPresent": {
"metadataNamespace": "consul",
"key": "service",
"regexValueRewrite": {
"pattern": {
"googleRe2": {},
"regex": ".*URI=spiffe://([^/]+.[^/]+)(?:/ap/([^/]+))?/ns/([^/]+)/dc/([^/]+)/svc/([^/;,]+).*"
},
"substitution": "\\5"
}
}
}
]
}
},
{
"name": "envoy.filters.http.router",
"typedConfig": {
"@type": "type.googleapis.com/envoy.extensions.filters.http.router.v3.Router"
}
}
],
"tracing": {
"randomSampling": {}
},
"forwardClientCertDetails": "APPEND_FORWARD",
"setCurrentClientCertDetails": {
"subject": true,
"cert": true,
"chain": true,
"dns": true,
"uri": true
}
}
}
],
"transportSocket": {
"name": "tls",
"typedConfig": {
"@type": "type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.DownstreamTlsContext",
"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"
}
},
"alpnProtocols": [
"http/1.1"
]
},
"requireClientCertificate": true
}
}
}
],
"trafficDirection": "INBOUND"
}
],
"typeUrl": "type.googleapis.com/envoy.config.listener.v3.Listener",
"nonce": "00000001"
}

View File

@ -0,0 +1,252 @@
{
"versionInfo": "00000001",
"resources": [
{
"@type": "type.googleapis.com/envoy.config.listener.v3.Listener",
"name": "db:127.0.0.1:9191",
"address": {
"socketAddress": {
"address": "127.0.0.1",
"portValue": 9191
}
},
"statPrefix": "custom.stats.outbound.only",
"filterChains": [
{
"filters": [
{
"name": "envoy.filters.network.http_connection_manager",
"typedConfig": {
"@type": "type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager",
"statPrefix": "upstream.db.default.default.dc1",
"rds": {
"configSource": {
"ads": {},
"resourceApiVersion": "V3"
},
"routeConfigName": "db"
},
"httpFilters": [
{
"name": "envoy.filters.http.router",
"typedConfig": {
"@type": "type.googleapis.com/envoy.extensions.filters.http.router.v3.Router"
}
}
],
"tracing": {
"randomSampling": {}
}
}
}
]
}
],
"trafficDirection": "OUTBOUND"
},
{
"@type": "type.googleapis.com/envoy.config.listener.v3.Listener",
"name": "prepared_query:geo-cache:127.10.10.10:8181",
"address": {
"socketAddress": {
"address": "127.10.10.10",
"portValue": 8181
}
},
"filterChains": [
{
"filters": [
{
"name": "envoy.filters.network.tcp_proxy",
"typedConfig": {
"@type": "type.googleapis.com/envoy.extensions.filters.network.tcp_proxy.v3.TcpProxy",
"statPrefix": "upstream.prepared_query_geo-cache",
"cluster": "geo-cache.default.dc1.query.11111111-2222-3333-4444-555555555555.consul"
}
}
]
}
],
"trafficDirection": "OUTBOUND"
},
{
"@type": "type.googleapis.com/envoy.config.listener.v3.Listener",
"name": "public_listener:0.0.0.0:9999",
"address": {
"socketAddress": {
"address": "0.0.0.0",
"portValue": 9999
}
},
"filterChains": [
{
"filters": [
{
"name": "envoy.filters.network.http_connection_manager",
"typedConfig": {
"@type": "type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager",
"statPrefix": "public_listener",
"routeConfig": {
"name": "public_listener",
"virtualHosts": [
{
"name": "public_listener",
"domains": [
"*"
],
"routes": [
{
"match": {
"prefix": "/"
},
"route": {
"cluster": "local_app"
}
}
]
}
]
},
"httpFilters": [
{
"name": "envoy.filters.http.rbac",
"typedConfig": {
"@type": "type.googleapis.com/envoy.extensions.filters.http.rbac.v3.RBAC",
"rules": {}
}
},
{
"name": "envoy.filters.http.header_to_metadata",
"typedConfig": {
"@type": "type.googleapis.com/envoy.extensions.filters.http.header_to_metadata.v3.Config",
"requestRules": [
{
"header": "x-forwarded-client-cert",
"onHeaderPresent": {
"metadataNamespace": "consul",
"key": "trust-domain",
"regexValueRewrite": {
"pattern": {
"googleRe2": {},
"regex": ".*URI=spiffe://([^/]+.[^/]+)(?:/ap/([^/]+))?/ns/([^/]+)/dc/([^/]+)/svc/([^/;,]+).*"
},
"substitution": "\\1"
}
}
},
{
"header": "x-forwarded-client-cert",
"onHeaderPresent": {
"metadataNamespace": "consul",
"key": "partition",
"regexValueRewrite": {
"pattern": {
"googleRe2": {},
"regex": ".*URI=spiffe://([^/]+.[^/]+)(?:/ap/([^/]+))?/ns/([^/]+)/dc/([^/]+)/svc/([^/;,]+).*"
},
"substitution": "\\2"
}
}
},
{
"header": "x-forwarded-client-cert",
"onHeaderPresent": {
"metadataNamespace": "consul",
"key": "namespace",
"regexValueRewrite": {
"pattern": {
"googleRe2": {},
"regex": ".*URI=spiffe://([^/]+.[^/]+)(?:/ap/([^/]+))?/ns/([^/]+)/dc/([^/]+)/svc/([^/;,]+).*"
},
"substitution": "\\3"
}
}
},
{
"header": "x-forwarded-client-cert",
"onHeaderPresent": {
"metadataNamespace": "consul",
"key": "datacenter",
"regexValueRewrite": {
"pattern": {
"googleRe2": {},
"regex": ".*URI=spiffe://([^/]+.[^/]+)(?:/ap/([^/]+))?/ns/([^/]+)/dc/([^/]+)/svc/([^/;,]+).*"
},
"substitution": "\\4"
}
}
},
{
"header": "x-forwarded-client-cert",
"onHeaderPresent": {
"metadataNamespace": "consul",
"key": "service",
"regexValueRewrite": {
"pattern": {
"googleRe2": {},
"regex": ".*URI=spiffe://([^/]+.[^/]+)(?:/ap/([^/]+))?/ns/([^/]+)/dc/([^/]+)/svc/([^/;,]+).*"
},
"substitution": "\\5"
}
}
}
]
}
},
{
"name": "envoy.filters.http.router",
"typedConfig": {
"@type": "type.googleapis.com/envoy.extensions.filters.http.router.v3.Router"
}
}
],
"tracing": {
"randomSampling": {}
},
"forwardClientCertDetails": "APPEND_FORWARD",
"setCurrentClientCertDetails": {
"subject": true,
"cert": true,
"chain": true,
"dns": true,
"uri": true
}
}
}
],
"transportSocket": {
"name": "tls",
"typedConfig": {
"@type": "type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.DownstreamTlsContext",
"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"
}
},
"alpnProtocols": [
"http/1.1"
]
},
"requireClientCertificate": true
}
}
}
],
"trafficDirection": "INBOUND"
}
],
"typeUrl": "type.googleapis.com/envoy.config.listener.v3.Listener",
"nonce": "00000001"
}

View File

@ -0,0 +1,234 @@
{
"versionInfo": "00000001",
"resources": [
{
"@type": "type.googleapis.com/envoy.config.listener.v3.Listener",
"name": "db:127.0.0.1:9191",
"address": {
"socketAddress": {
"address": "127.0.0.1",
"portValue": 9191
}
},
"filterChains": [
{
"filters": [
{
"name": "envoy.filters.network.tcp_proxy",
"typedConfig": {
"@type": "type.googleapis.com/envoy.extensions.filters.network.tcp_proxy.v3.TcpProxy",
"statPrefix": "upstream.db.default.default.dc1",
"cluster": "db.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul"
}
}
]
}
],
"trafficDirection": "OUTBOUND"
},
{
"@type": "type.googleapis.com/envoy.config.listener.v3.Listener",
"name": "prepared_query:geo-cache:127.10.10.10:8181",
"address": {
"socketAddress": {
"address": "127.10.10.10",
"portValue": 8181
}
},
"filterChains": [
{
"filters": [
{
"name": "envoy.filters.network.tcp_proxy",
"typedConfig": {
"@type": "type.googleapis.com/envoy.extensions.filters.network.tcp_proxy.v3.TcpProxy",
"statPrefix": "upstream.prepared_query_geo-cache",
"cluster": "geo-cache.default.dc1.query.11111111-2222-3333-4444-555555555555.consul"
}
}
]
}
],
"trafficDirection": "OUTBOUND"
},
{
"@type": "type.googleapis.com/envoy.config.listener.v3.Listener",
"name": "public_listener:0.0.0.0:9999",
"address": {
"socketAddress": {
"address": "0.0.0.0",
"portValue": 9999
}
},
"filterChains": [
{
"filters": [
{
"name": "envoy.filters.network.http_connection_manager",
"typedConfig": {
"@type": "type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager",
"statPrefix": "public_listener",
"routeConfig": {
"name": "public_listener",
"virtualHosts": [
{
"name": "public_listener",
"domains": [
"*"
],
"routes": [
{
"match": {
"prefix": "/"
},
"route": {
"cluster": "local_app"
}
}
]
}
]
},
"httpFilters": [
{
"name": "envoy.filters.http.rbac",
"typedConfig": {
"@type": "type.googleapis.com/envoy.extensions.filters.http.rbac.v3.RBAC",
"rules": {}
}
},
{
"name": "envoy.filters.http.header_to_metadata",
"typedConfig": {
"@type": "type.googleapis.com/envoy.extensions.filters.http.header_to_metadata.v3.Config",
"requestRules": [
{
"header": "x-forwarded-client-cert",
"onHeaderPresent": {
"metadataNamespace": "consul",
"key": "trust-domain",
"regexValueRewrite": {
"pattern": {
"googleRe2": {},
"regex": ".*URI=spiffe://([^/]+.[^/]+)(?:/ap/([^/]+))?/ns/([^/]+)/dc/([^/]+)/svc/([^/;,]+).*"
},
"substitution": "\\1"
}
}
},
{
"header": "x-forwarded-client-cert",
"onHeaderPresent": {
"metadataNamespace": "consul",
"key": "partition",
"regexValueRewrite": {
"pattern": {
"googleRe2": {},
"regex": ".*URI=spiffe://([^/]+.[^/]+)(?:/ap/([^/]+))?/ns/([^/]+)/dc/([^/]+)/svc/([^/;,]+).*"
},
"substitution": "\\2"
}
}
},
{
"header": "x-forwarded-client-cert",
"onHeaderPresent": {
"metadataNamespace": "consul",
"key": "namespace",
"regexValueRewrite": {
"pattern": {
"googleRe2": {},
"regex": ".*URI=spiffe://([^/]+.[^/]+)(?:/ap/([^/]+))?/ns/([^/]+)/dc/([^/]+)/svc/([^/;,]+).*"
},
"substitution": "\\3"
}
}
},
{
"header": "x-forwarded-client-cert",
"onHeaderPresent": {
"metadataNamespace": "consul",
"key": "datacenter",
"regexValueRewrite": {
"pattern": {
"googleRe2": {},
"regex": ".*URI=spiffe://([^/]+.[^/]+)(?:/ap/([^/]+))?/ns/([^/]+)/dc/([^/]+)/svc/([^/;,]+).*"
},
"substitution": "\\4"
}
}
},
{
"header": "x-forwarded-client-cert",
"onHeaderPresent": {
"metadataNamespace": "consul",
"key": "service",
"regexValueRewrite": {
"pattern": {
"googleRe2": {},
"regex": ".*URI=spiffe://([^/]+.[^/]+)(?:/ap/([^/]+))?/ns/([^/]+)/dc/([^/]+)/svc/([^/;,]+).*"
},
"substitution": "\\5"
}
}
}
]
}
},
{
"name": "envoy.filters.http.router",
"typedConfig": {
"@type": "type.googleapis.com/envoy.extensions.filters.http.router.v3.Router"
}
}
],
"tracing": {
"randomSampling": {}
},
"forwardClientCertDetails": "APPEND_FORWARD",
"setCurrentClientCertDetails": {
"subject": true,
"cert": true,
"chain": true,
"dns": true,
"uri": true
}
}
}
],
"transportSocket": {
"name": "tls",
"typedConfig": {
"@type": "type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.DownstreamTlsContext",
"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"
}
},
"alpnProtocols": [
"http/1.1"
]
},
"requireClientCertificate": true
}
}
}
],
"trafficDirection": "INBOUND"
}
],
"typeUrl": "type.googleapis.com/envoy.config.listener.v3.Listener",
"nonce": "00000001"
}

View File

@ -0,0 +1,32 @@
{
"versionInfo": "00000001",
"resources": [
{
"@type": "type.googleapis.com/envoy.config.route.v3.RouteConfiguration",
"name": "db",
"virtualHosts": [
{
"name": "db",
"domains": [
"*"
],
"routes": [
{
"match": {
"prefix": "/"
},
"route": {
"cluster": "db.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",
"timeout": "33s"
}
}
]
}
],
"mostSpecificHeaderMutationsWins": true,
"validateClusters": true
}
],
"typeUrl": "type.googleapis.com/envoy.config.route.v3.RouteConfiguration",
"nonce": "00000001"
}

View File

@ -0,0 +1,43 @@
{
"versionInfo": "00000001",
"resources": [
{
"@type": "type.googleapis.com/envoy.config.route.v3.RouteConfiguration",
"name": "db",
"virtualHosts": [
{
"name": "db",
"domains": [
"*"
],
"routes": [
{
"match": {
"prefix": "/"
},
"route": {
"weightedClusters": {
"clusters": [
{
"name": "v1.db.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",
"weight": 5000
},
{
"name": "v2.db.default.dc2.internal.11111111-2222-3333-4444-555555555555.consul",
"weight": 5000
}
],
"totalWeight": 10000
}
}
}
]
}
],
"mostSpecificHeaderMutationsWins": true,
"validateClusters": true
}
],
"typeUrl": "type.googleapis.com/envoy.config.route.v3.RouteConfiguration",
"nonce": "00000001"
}

View File

@ -0,0 +1,31 @@
{
"versionInfo": "00000001",
"resources": [
{
"@type": "type.googleapis.com/envoy.config.route.v3.RouteConfiguration",
"name": "db",
"virtualHosts": [
{
"name": "db",
"domains": [
"*"
],
"routes": [
{
"match": {
"prefix": "/"
},
"route": {
"cluster": "db.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",
"timeout": "33s"
}
}
]
}
],
"validateClusters": true
}
],
"typeUrl": "type.googleapis.com/envoy.config.route.v3.RouteConfiguration",
"nonce": "00000001"
}

View File

@ -38,7 +38,7 @@ type BasicExtension interface {
// PatchRoute patches a route to include the custom Envoy configuration
// required to integrate with the built in extension template.
// See also PatchRoutes.
PatchRoute(*RuntimeConfig, *envoy_route_v3.RouteConfiguration) (*envoy_route_v3.RouteConfiguration, bool, error)
PatchRoute(RoutePayload) (*envoy_route_v3.RouteConfiguration, bool, error)
// PatchRoutes patches routes to include the custom Envoy configuration
// required to integrate with the built in extension template.
@ -51,7 +51,7 @@ type BasicExtension interface {
// PatchCluster patches a cluster to include the custom Envoy configuration
// required to integrate with the built in extension template.
// See also PatchClusters.
PatchCluster(*RuntimeConfig, *envoy_cluster_v3.Cluster) (*envoy_cluster_v3.Cluster, bool, error)
PatchCluster(ClusterPayload) (*envoy_cluster_v3.Cluster, bool, error)
// PatchClusters patches clusters to include the custom Envoy configuration
// required to integrate with the built in extension template.
@ -63,12 +63,12 @@ type BasicExtension interface {
// PatchClusterLoadAssignment patches a cluster load assignment to include the custom Envoy configuration
// required to integrate with the built in extension template.
PatchClusterLoadAssignment(*RuntimeConfig, *envoy_endpoint_v3.ClusterLoadAssignment) (*envoy_endpoint_v3.ClusterLoadAssignment, bool, error)
PatchClusterLoadAssignment(ClusterLoadAssignmentPayload) (*envoy_endpoint_v3.ClusterLoadAssignment, bool, error)
// PatchListener patches a listener to include the custom Envoy configuration
// required to integrate with the built in extension template.
// See also PatchListeners.
PatchListener(*RuntimeConfig, *envoy_listener_v3.Listener) (*envoy_listener_v3.Listener, bool, error)
PatchListener(ListenerPayload) (*envoy_listener_v3.Listener, bool, error)
// PatchListeners patches listeners to include the custom Envoy configuration
// required to integrate with the built in extension template.
@ -81,7 +81,7 @@ type BasicExtension interface {
// PatchFilter patches an Envoy filter to include the custom Envoy
// configuration required to integrate with the built in extension template.
// See also PatchFilters.
PatchFilter(cfg *RuntimeConfig, f *envoy_listener_v3.Filter, isInboundListener bool) (*envoy_listener_v3.Filter, bool, error)
PatchFilter(FilterPayload) (*envoy_listener_v3.Filter, bool, error)
// PatchFilters patches Envoy filters to include the custom Envoy
// configuration required to integrate with the built in extension template.
@ -197,7 +197,7 @@ func (b *BasicEnvoyExtender) patchClusters(config *RuntimeConfig, clusters Clust
return clusters, fmt.Errorf("error patching clusters: %w", err)
}
for nameOrSNI, cluster := range clusters {
patchedCluster, patched, err := b.Extension.PatchCluster(config, cluster)
patchedCluster, patched, err := b.Extension.PatchCluster(config.GetClusterPayload(cluster))
if err != nil {
resultErr = multierror.Append(resultErr, fmt.Errorf("error patching cluster %q: %w", nameOrSNI, err))
}
@ -208,7 +208,7 @@ func (b *BasicEnvoyExtender) patchClusters(config *RuntimeConfig, clusters Clust
// We patch cluster load assignments directly above for EDS, but also here for CDS,
// since updates can come from either.
if patchedCluster.LoadAssignment != nil {
patchedClusterLoadAssignment, patched, err := b.Extension.PatchClusterLoadAssignment(config, patchedCluster.LoadAssignment)
patchedClusterLoadAssignment, patched, err := b.Extension.PatchClusterLoadAssignment(config.GetClusterLoadAssignmentPayload(patchedCluster.LoadAssignment))
if err != nil {
resultErr = multierror.Append(resultErr, fmt.Errorf("error patching load assignment for cluster %q: %w", nameOrSNI, err))
} else if patched {
@ -225,7 +225,7 @@ func (b *BasicEnvoyExtender) patchClusterLoadAssignments(config *RuntimeConfig,
var resultErr error
for nameOrSNI, clusterLoadAssignment := range clusterLoadAssignments {
patchedClusterLoadAssignment, patched, err := b.Extension.PatchClusterLoadAssignment(config, clusterLoadAssignment)
patchedClusterLoadAssignment, patched, err := b.Extension.PatchClusterLoadAssignment(config.GetClusterLoadAssignmentPayload(clusterLoadAssignment))
if err != nil {
resultErr = multierror.Append(resultErr, fmt.Errorf("error patching cluster load assignment %q: %w", nameOrSNI, err))
}
@ -246,7 +246,7 @@ func (b *BasicEnvoyExtender) patchRoutes(config *RuntimeConfig, routes RouteMap)
return routes, fmt.Errorf("error patching routes: %w", err)
}
for nameOrSNI, route := range patchedRoutes {
patchedRoute, patched, err := b.Extension.PatchRoute(config, route)
patchedRoute, patched, err := b.Extension.PatchRoute(config.GetRoutePayload(route))
if err != nil {
resultErr = multierror.Append(resultErr, fmt.Errorf("error patching route %q: %w", nameOrSNI, err))
}
@ -267,7 +267,7 @@ func (b *BasicEnvoyExtender) patchListeners(config *RuntimeConfig, listeners Lis
return listeners, fmt.Errorf("error patching listeners: %w", err)
}
for nameOrSNI, listener := range listeners {
patchedListener, patched, err := b.Extension.PatchListener(config, listener)
patchedListener, patched, err := b.Extension.PatchListener(config.GetListenerPayload(listener))
if err != nil {
resultErr = multierror.Append(resultErr, fmt.Errorf("error patching listener %q: %w", nameOrSNI, err))
}
@ -298,7 +298,7 @@ func (b *BasicEnvoyExtender) patchListenerFilterChains(config *RuntimeConfig, l
func (b *BasicEnvoyExtender) patchTerminatingGatewayListenerFilterChains(config *RuntimeConfig, l *envoy_listener_v3.Listener, nameOrSNI string) (*envoy_listener_v3.Listener, error) {
var resultErr error
for idx, filterChain := range l.FilterChains {
if patchedFilterChain, err := b.patchFilterChain(config, filterChain, IsInboundPublicListener(l)); err == nil {
if patchedFilterChain, err := b.patchFilterChain(config, filterChain, l); err == nil {
l.FilterChains[idx] = patchedFilterChain
} else {
resultErr = multierror.Append(resultErr, fmt.Errorf("error patching filter chain of terminating gateway listener %q: %w", nameOrSNI, err))
@ -330,10 +330,8 @@ func (b *BasicEnvoyExtender) patchConnectProxyListenerFilterChains(config *Runti
func (b *BasicEnvoyExtender) patchNonTProxyConnectProxyListenerFilterChains(config *RuntimeConfig, l *envoy_listener_v3.Listener) (*envoy_listener_v3.Listener, error) {
var resultErr error
inbound := IsInboundPublicListener(l)
for idx, filterChain := range l.FilterChains {
if patchedFilterChain, err := b.patchFilterChain(config, filterChain, inbound); err == nil {
if patchedFilterChain, err := b.patchFilterChain(config, filterChain, l); err == nil {
l.FilterChains[idx] = patchedFilterChain
} else {
resultErr = multierror.Append(resultErr, fmt.Errorf("error patching filter chain: %w", err))
@ -346,7 +344,6 @@ func (b *BasicEnvoyExtender) patchTProxyListenerFilterChains(config *RuntimeConf
var resultErr error
vip := config.Upstreams[config.ServiceName].VIP
inbound := IsInboundPublicListener(l)
for idx, filterChain := range l.FilterChains {
match := filterChainTProxyMatch(vip, filterChain)
@ -354,7 +351,7 @@ func (b *BasicEnvoyExtender) patchTProxyListenerFilterChains(config *RuntimeConf
continue
}
if patchedFilterChain, err := b.patchFilterChain(config, filterChain, inbound); err == nil {
if patchedFilterChain, err := b.patchFilterChain(config, filterChain, l); err == nil {
l.FilterChains[idx] = patchedFilterChain
} else {
resultErr = multierror.Append(resultErr, fmt.Errorf("error patching filter chain for %q: %w", vip, err))
@ -364,14 +361,15 @@ func (b *BasicEnvoyExtender) patchTProxyListenerFilterChains(config *RuntimeConf
return l, resultErr
}
func (b *BasicEnvoyExtender) patchFilterChain(config *RuntimeConfig, filterChain *envoy_listener_v3.FilterChain, isInboundListener bool) (*envoy_listener_v3.FilterChain, error) {
func (b *BasicEnvoyExtender) patchFilterChain(config *RuntimeConfig, filterChain *envoy_listener_v3.FilterChain, l *envoy_listener_v3.Listener) (*envoy_listener_v3.FilterChain, error) {
var resultErr error
patchedFilters, err := b.Extension.PatchFilters(config, filterChain.Filters, isInboundListener)
inbound := IsInboundPublicListener(l)
patchedFilters, err := b.Extension.PatchFilters(config, filterChain.Filters, inbound)
if err != nil {
return filterChain, fmt.Errorf("error patching filters: %w", err)
}
for idx, filter := range patchedFilters {
patchedFilter, patched, err := b.Extension.PatchFilter(config, filter, isInboundListener)
patchedFilter, patched, err := b.Extension.PatchFilter(config.GetFilterPayload(filter, l))
if err != nil {
resultErr = multierror.Append(resultErr, fmt.Errorf("error patching filter: %w", err))
}

View File

@ -20,8 +20,8 @@ type BasicExtensionAdapter struct{}
func (BasicExtensionAdapter) CanApply(_ *RuntimeConfig) bool { return false }
// PatchCluster provides a default implementation of the PatchCluster interface that does nothing.
func (BasicExtensionAdapter) PatchCluster(_ *RuntimeConfig, c *envoy_cluster_v3.Cluster) (*envoy_cluster_v3.Cluster, bool, error) {
return c, false, nil
func (BasicExtensionAdapter) PatchCluster(p ClusterPayload) (*envoy_cluster_v3.Cluster, bool, error) {
return p.Message, false, nil
}
// PatchClusters provides a default implementation of the PatchClusters interface that does nothing.
@ -30,13 +30,13 @@ func (BasicExtensionAdapter) PatchClusters(_ *RuntimeConfig, c ClusterMap) (Clus
}
// PatchClusterLoadAssignment provides a default implementation of the PatchClusterLoadAssignment interface that does nothing.
func (BasicExtensionAdapter) PatchClusterLoadAssignment(_ *RuntimeConfig, c *envoy_endpoint_v3.ClusterLoadAssignment) (*envoy_endpoint_v3.ClusterLoadAssignment, bool, error) {
return c, false, nil
func (BasicExtensionAdapter) PatchClusterLoadAssignment(p ClusterLoadAssignmentPayload) (*envoy_endpoint_v3.ClusterLoadAssignment, bool, error) {
return p.Message, false, nil
}
// PatchListener provides a default implementation of the PatchListener interface that does nothing.
func (BasicExtensionAdapter) PatchListener(_ *RuntimeConfig, l *envoy_listener_v3.Listener) (*envoy_listener_v3.Listener, bool, error) {
return l, false, nil
func (BasicExtensionAdapter) PatchListener(p ListenerPayload) (*envoy_listener_v3.Listener, bool, error) {
return p.Message, false, nil
}
// PatchListeners provides a default implementation of the PatchListeners interface that does nothing.
@ -45,8 +45,8 @@ func (BasicExtensionAdapter) PatchListeners(_ *RuntimeConfig, l ListenerMap) (Li
}
// PatchFilter provides a default implementation of the PatchFilter interface that does nothing.
func (BasicExtensionAdapter) PatchFilter(_ *RuntimeConfig, f *envoy_listener_v3.Filter, _ bool) (*envoy_listener_v3.Filter, bool, error) {
return f, false, nil
func (BasicExtensionAdapter) PatchFilter(p FilterPayload) (*envoy_listener_v3.Filter, bool, error) {
return p.Message, false, nil
}
// PatchFilters provides a default implementation of the PatchFilters interface that does nothing.
@ -55,8 +55,8 @@ func (BasicExtensionAdapter) PatchFilters(_ *RuntimeConfig, f []*envoy_listener_
}
// PatchRoute provides a default implementation of the PatchRoute interface that does nothing.
func (BasicExtensionAdapter) PatchRoute(_ *RuntimeConfig, r *envoy_route_v3.RouteConfiguration) (*envoy_route_v3.RouteConfiguration, bool, error) {
return r, false, nil
func (BasicExtensionAdapter) PatchRoute(p RoutePayload) (*envoy_route_v3.RouteConfiguration, bool, error) {
return p.Message, false, nil
}
// PatchRoutes provides a default implementation of the PatchRoutes interface that does nothing.

View File

@ -17,9 +17,11 @@ import (
envoy_tcp_proxy_v3 "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/tcp_proxy/v3"
envoy_tls_v3 "github.com/envoyproxy/go-control-plane/envoy/extensions/transport_sockets/tls/v3"
envoy_resource_v3 "github.com/envoyproxy/go-control-plane/pkg/resource/v3"
"github.com/hashicorp/consul/envoyextensions/xdscommon"
"google.golang.org/protobuf/proto"
"google.golang.org/protobuf/types/known/anypb"
"github.com/hashicorp/consul/api"
"github.com/hashicorp/consul/envoyextensions/xdscommon"
)
// MakeUpstreamTLSTransportSocket generates an Envoy transport socket for the given TLS context.
@ -70,6 +72,206 @@ func MakeFilter(name string, cfg proto.Message) (*envoy_listener_v3.Filter, erro
}, nil
}
// TrafficDirection determines whether inbound or outbound Envoy resources will be patched.
type TrafficDirection string
const (
TrafficDirectionInbound TrafficDirection = "inbound"
TrafficDirectionOutbound TrafficDirection = "outbound"
)
var TrafficDirections = StringSet{string(TrafficDirectionInbound): {}, string(TrafficDirectionOutbound): {}}
type StringSet map[string]struct{}
func (c *StringSet) CheckRequired(v, fieldName string) error {
if _, ok := (*c)[v]; !ok {
if v == "" {
return fmt.Errorf("field %s is required", fieldName)
}
var keys []string
for k := range *c {
keys = append(keys, k)
}
return fmt.Errorf("invalid %s '%q'; supported values: %s",
fieldName, v, strings.Join(keys, ", "))
}
return nil
}
func NormalizeEmptyToDefault(s string) string {
if s == "" {
return "default"
}
return s
}
func NormalizeServiceName(sn *api.CompoundServiceName) {
sn.Namespace = NormalizeEmptyToDefault(sn.Namespace)
sn.Partition = NormalizeEmptyToDefault(sn.Partition)
}
// Payload represents a single Envoy resource to be modified by extensions.
// It associates the RuntimeConfig of the local proxy, the TrafficDirection
// of the resource, and the CompoundServiceName and UpstreamData (if outbound)
// of a service the Envoy resource corresponds to.
type Payload[K proto.Message] struct {
RuntimeConfig *RuntimeConfig
ServiceName *api.CompoundServiceName
Upstream *UpstreamData
TrafficDirection TrafficDirection
Message K
}
func (p Payload[K]) IsInbound() bool {
return p.TrafficDirection == TrafficDirectionInbound
}
type ClusterPayload = Payload[*envoy_cluster_v3.Cluster]
type ClusterLoadAssignmentPayload = Payload[*envoy_endpoint_v3.ClusterLoadAssignment]
type ListenerPayload = Payload[*envoy_listener_v3.Listener]
type FilterPayload = Payload[*envoy_listener_v3.Filter]
type RoutePayload = Payload[*envoy_route_v3.RouteConfiguration]
func (cfg *RuntimeConfig) GetClusterPayload(c *envoy_cluster_v3.Cluster) ClusterPayload {
d := TrafficDirectionOutbound
var u *UpstreamData
var sn *api.CompoundServiceName
if IsLocalAppCluster(c) {
d = TrafficDirectionInbound
} else {
u, sn = cfg.findUpstreamBySNI(c.Name)
}
return ClusterPayload{
RuntimeConfig: cfg,
ServiceName: sn,
Upstream: u,
TrafficDirection: d,
Message: c,
}
}
func (c *RuntimeConfig) GetListenerPayload(l *envoy_listener_v3.Listener) ListenerPayload {
d := TrafficDirectionOutbound
var u *UpstreamData
var sn *api.CompoundServiceName
if IsInboundPublicListener(l) {
d = TrafficDirectionInbound
} else {
u, sn = c.findUpstreamByEnvoyID(GetListenerEnvoyID(l))
}
return ListenerPayload{
RuntimeConfig: c,
ServiceName: sn,
Upstream: u,
TrafficDirection: d,
Message: l,
}
}
func (c *RuntimeConfig) GetFilterPayload(f *envoy_listener_v3.Filter, l *envoy_listener_v3.Listener) FilterPayload {
d := TrafficDirectionOutbound
var u *UpstreamData
var sn *api.CompoundServiceName
if IsInboundPublicListener(l) {
d = TrafficDirectionInbound
} else {
u, sn = c.findUpstreamByEnvoyID(GetListenerEnvoyID(l))
}
return FilterPayload{
RuntimeConfig: c,
ServiceName: sn,
Upstream: u,
TrafficDirection: d,
Message: f,
}
}
func (c *RuntimeConfig) GetRoutePayload(r *envoy_route_v3.RouteConfiguration) RoutePayload {
d := TrafficDirectionOutbound
var u *UpstreamData
var sn *api.CompoundServiceName
if IsRouteToLocalAppCluster(r) {
d = TrafficDirectionInbound
} else {
u, sn = c.findUpstreamByEnvoyID(r.Name)
}
return RoutePayload{
RuntimeConfig: c,
ServiceName: sn,
Upstream: u,
TrafficDirection: d,
Message: r,
}
}
func (cfg *RuntimeConfig) GetClusterLoadAssignmentPayload(c *envoy_endpoint_v3.ClusterLoadAssignment) ClusterLoadAssignmentPayload {
d := TrafficDirectionOutbound
var u *UpstreamData
var sn *api.CompoundServiceName
if IsLocalAppClusterLoadAssignment(c) {
d = TrafficDirectionInbound
} else {
u, sn = cfg.findUpstreamBySNI(c.ClusterName)
}
return ClusterLoadAssignmentPayload{
RuntimeConfig: cfg,
ServiceName: sn,
Upstream: u,
TrafficDirection: d,
Message: c,
}
}
func (c *RuntimeConfig) findUpstreamByEnvoyID(envoyID string) (*UpstreamData, *api.CompoundServiceName) {
for sn, u := range c.Upstreams {
if u.EnvoyID == envoyID {
return u, &sn
}
}
return nil, nil
}
func (c *RuntimeConfig) findUpstreamBySNI(sni string) (*UpstreamData, *api.CompoundServiceName) {
for sn, u := range c.Upstreams {
_, ok := u.SNIs[sni]
if ok {
return u, &sn
}
if strings.HasPrefix(sni, xdscommon.FailoverClusterNamePrefix) {
parts := strings.Split(sni, "~")
if len(parts) != 3 {
continue
}
id := parts[2]
_, ok := u.SNIs[id]
if ok {
return u, &sn
}
}
}
return nil, nil
}
// GetListenerEnvoyID returns the Envoy ID string parsed from the name of the given Listener. If none is found, it
// returns the empty string.
func GetListenerEnvoyID(l *envoy_listener_v3.Listener) string {

View File

@ -8,8 +8,11 @@ import "github.com/hashicorp/consul/api"
// UpstreamData has the SNI, EnvoyID, and OutgoingProxyKind of the upstream services for the local proxy and this data
// is used to choose which Envoy resources to patch.
type UpstreamData struct {
// SNI is the SNI header used to reach an upstream service.
SNI map[string]struct{}
// This is the SNI for the upstream service without accounting for any discovery chain magic.
PrimarySNI string
// SNIs is the SNIs header used to reach an upstream service.
SNIs map[string]struct{}
// EnvoyID is the envoy ID of an upstream service, structured <service> or <partition>/<ns>/<service> when using a
// non-default namespace or partition.
@ -68,7 +71,7 @@ type RuntimeConfig struct {
// Only used when IsSourcedFromUpstream is true.
func (c RuntimeConfig) MatchesUpstreamServiceSNI(sni string) bool {
u := c.Upstreams[c.ServiceName]
_, match := u.SNI[sni]
_, match := u.SNIs[sni]
return match
}

View File

@ -20,7 +20,7 @@ func makeTestRuntimeConfig() RuntimeConfig {
sn: {
EnvoyID: "eid",
OutgoingProxyKind: api.ServiceKindTerminatingGateway,
SNI: map[string]struct{}{
SNIs: map[string]struct{}{
"sni1": {},
"sni2": {},
},

View File

@ -74,7 +74,7 @@ func (ext *UpstreamEnvoyExtender) Extend(resources *xdscommon.IndexedResources,
continue
}
newCluster, patched, err := ext.Extension.PatchCluster(config, resource)
newCluster, patched, err := ext.Extension.PatchCluster(config.GetClusterPayload(resource))
if err != nil {
resultErr = multierror.Append(resultErr, fmt.Errorf("error patching cluster: %w", err))
continue
@ -101,7 +101,7 @@ func (ext *UpstreamEnvoyExtender) Extend(resources *xdscommon.IndexedResources,
continue
}
newRoute, patched, err := ext.Extension.PatchRoute(config, resource)
newRoute, patched, err := ext.Extension.PatchRoute(config.GetRoutePayload(resource))
if err != nil {
resultErr = multierror.Append(resultErr, fmt.Errorf("error patching route: %w", err))
continue
@ -146,7 +146,7 @@ func (ext *UpstreamEnvoyExtender) patchTerminatingGatewayListener(config *Runtim
var filters []*envoy_listener_v3.Filter
for _, filter := range filterChain.Filters {
newFilter, ok, err := ext.Extension.PatchFilter(config, filter, IsInboundPublicListener(l))
newFilter, ok, err := ext.Extension.PatchFilter(config.GetFilterPayload(filter, l))
if err != nil {
resultErr = multierror.Append(resultErr, fmt.Errorf("error patching listener filter: %w", err))
@ -190,7 +190,7 @@ func (ext *UpstreamEnvoyExtender) patchConnectProxyListener(config *RuntimeConfi
var filters []*envoy_listener_v3.Filter
for _, filter := range filterChain.Filters {
newFilter, ok, err := ext.Extension.PatchFilter(config, filter, IsInboundPublicListener(l))
newFilter, ok, err := ext.Extension.PatchFilter(config.GetFilterPayload(filter, l))
if err != nil {
resultErr = multierror.Append(resultErr, fmt.Errorf("error patching listener filter: %w", err))
filters = append(filters, filter)
@ -225,7 +225,7 @@ func (ext *UpstreamEnvoyExtender) patchTProxyListener(config *RuntimeConfig, l *
}
for _, filter := range filterChain.Filters {
newFilter, ok, err := ext.Extension.PatchFilter(config, filter, IsInboundPublicListener(l))
newFilter, ok, err := ext.Extension.PatchFilter(config.GetFilterPayload(filter, l))
if err != nil {
resultErr = multierror.Append(resultErr, fmt.Errorf("error patching listener filter: %w", err))
filters = append(filters, filter)

View File

@ -52,6 +52,8 @@ const (
// SecretType is the TypeURL for Secret discovery responses.
SecretType = apiTypePrefix + "envoy.extensions.transport_sockets.tls.v3.Secret"
FailoverClusterNamePrefix = "failover-target~"
)
type IndexedResources struct {

View File

@ -87,7 +87,7 @@ func Validate(indexedResources *xdscommon.IndexedResources, envoyID string, vip
// the cluster SNIs configured on this proxy, not just the upstream being validated. This means the
// PatchCluster function in the Validate plugin will be run on all clusters, but errors will only
// surface for clusters related to the upstream being validated.
SNI: snis,
SNIs: snis,
EnvoyID: envoyID,
},
},

View File

@ -92,10 +92,10 @@ func MakeValidate(ext extensioncommon.RuntimeConfig) (extensioncommon.BasicExten
upstream, ok := ext.Upstreams[ext.ServiceName]
if ok {
vip = upstream.VIP
if upstream.SNI == nil || len(upstream.SNI) == 0 {
if upstream.SNIs == nil || len(upstream.SNIs) == 0 {
return nil, fmt.Errorf("no SNIs were set, unable to validate Envoy clusters")
}
snis = upstream.SNI
snis = upstream.SNIs
}
if mainEnvoyID == "" && vip == "" {
return nil, fmt.Errorf("envoyID or virtual IP is required")
@ -306,84 +306,87 @@ func (p *Validate) CanApply(config *extensioncommon.RuntimeConfig) bool {
return true
}
func (p *Validate) PatchRoute(config *extensioncommon.RuntimeConfig, route *envoy_route_v3.RouteConfiguration) (*envoy_route_v3.RouteConfiguration, bool, error) {
func (v *Validate) PatchRoute(p extensioncommon.RoutePayload) (*envoy_route_v3.RouteConfiguration, bool, error) {
route := p.Message
// Route name on connect proxies will be the envoy ID. We are only validating routes for the specific upstream with
// the envoyID configured.
if route.Name != p.envoyID {
if route.Name != p.Upstream.EnvoyID {
return route, false, nil
}
p.route = true
v.route = true
for sni := range extensioncommon.RouteClusterNames(route) {
if _, ok := p.resources[sni]; ok {
if _, ok := v.resources[sni]; ok {
continue
}
p.resources[sni] = &resource{required: true}
v.resources[sni] = &resource{required: true}
}
return route, false, nil
}
func (p *Validate) PatchCluster(config *extensioncommon.RuntimeConfig, c *envoy_cluster_v3.Cluster) (*envoy_cluster_v3.Cluster, bool, error) {
v, ok := p.resources[c.Name]
func (v *Validate) PatchCluster(p extensioncommon.ClusterPayload) (*envoy_cluster_v3.Cluster, bool, error) {
c := p.Message
val, ok := v.resources[c.Name]
if !ok {
v = &resource{}
p.resources[c.Name] = v
val = &resource{}
v.resources[c.Name] = val
}
v.cluster = true
val.cluster = true
// If it's an aggregate cluster, add the child clusters to p.resources if they are not already there.
aggregateCluster, ok := isAggregateCluster(c)
if ok {
// Mark this as an aggregate cluster, so we know we do not need to validate its endpoints directly.
v.aggregateCluster = true
val.aggregateCluster = true
for _, clusterName := range aggregateCluster.Clusters {
r, ok := p.resources[clusterName]
r, ok := v.resources[clusterName]
if !ok {
r = &resource{}
p.resources[clusterName] = r
v.resources[clusterName] = r
}
if v.aggregateClusterChildren == nil {
v.aggregateClusterChildren = []string{}
if val.aggregateClusterChildren == nil {
val.aggregateClusterChildren = []string{}
}
// On the parent cluster, add the children.
v.aggregateClusterChildren = append(v.aggregateClusterChildren, clusterName)
val.aggregateClusterChildren = append(val.aggregateClusterChildren, clusterName)
// On the child cluster, set the parent.
r.parentCluster = c.Name
// The child clusters of an aggregate cluster will be required if the parent cluster is.
r.required = v.required
r.required = val.required
}
return c, false, nil
}
if c.EdsClusterConfig != nil {
v.usesEDS = true
val.usesEDS = true
} else {
la := c.LoadAssignment
if la == nil {
return c, false, nil
}
v.endpoints = len(la.Endpoints) + len(la.NamedEndpoints)
val.endpoints = len(la.Endpoints) + len(la.NamedEndpoints)
}
return c, false, nil
}
func (p *Validate) PatchFilter(config *extensioncommon.RuntimeConfig, filter *envoy_listener_v3.Filter, _ bool) (*envoy_listener_v3.Filter, bool, error) {
func (v *Validate) PatchFilter(p extensioncommon.FilterPayload) (*envoy_listener_v3.Filter, bool, error) {
filter := p.Message
// If a single filter exists for a listener we say it exists.
p.listener = true
v.listener = true
if httpConfig := envoy_resource_v3.GetHTTPConnectionManager(filter); httpConfig != nil {
// If the http filter uses RDS, then the clusters we need to validate exist in the route, and there's nothing
// else we need to do with the filter.
if httpConfig.GetRds() != nil {
p.usesRDS = true
v.usesRDS = true
// Edit the runtime configuration to add an envoy ID based on the route name in the filter. This is because
// routes are matched by envoyID and in the transparent proxy case, we only have the VIP set in the
// RuntimeConfig.
p.envoyID = httpConfig.GetRds().RouteConfigName
v.envoyID = httpConfig.GetRds().RouteConfigName
emptyServiceKey := api.CompoundServiceName{}
upstream, ok := config.Upstreams[emptyServiceKey]
upstream, ok := p.RuntimeConfig.Upstreams[emptyServiceKey]
if ok {
upstream.EnvoyID = p.envoyID
upstream.EnvoyID = v.envoyID
}
return filter, true, nil
}
@ -392,10 +395,10 @@ func (p *Validate) PatchFilter(config *extensioncommon.RuntimeConfig, filter *en
// FilterClusterNames handles the filter being an http or tcp filter.
for sni := range extensioncommon.FilterClusterNames(filter) {
// Mark any clusters we see as required resources.
if r, ok := p.resources[sni]; ok {
if r, ok := v.resources[sni]; ok {
r.required = true
} else {
p.resources[sni] = &resource{required: true}
v.resources[sni] = &resource{required: true}
}
}

View File

@ -349,7 +349,7 @@ func TestMakeValidate(t *testing.T) {
},
Upstreams: map[api.CompoundServiceName]*extensioncommon.UpstreamData{
svc: {
SNI: tc.snis,
SNIs: tc.snis,
},
},
}