mirror of
https://github.com/pomerium/pomerium.git
synced 2025-07-16 16:26:16 +02:00
config: add circuit breaker thresholds (#5650)
## Summary Add a new `circuit_breaker_thresholds` option: ```yaml circuit_breaker_thresholds: max_connections: 1 max_pending_requests: 2 max_requests: 3 max_retries: 4 max_connection_pools: 5 ``` This option can be set at the global level or at the route level. Each threshold is optional and when not set a default will be used. For internal clusters we will disable the circuit breaker. For normal routes we will use the envoy defaults. ## Related issues - [ENG-2310](https://linear.app/pomerium/issue/ENG-2310/add-circuit-breaker-settings-per-route) ## Checklist - [x] reference any related issues - [x] updated unit tests - [x] add appropriate label (`enhancement`, `bug`, `breaking`, `dependencies`, `ci`) - [x] ready for review
This commit is contained in:
parent
e320a532de
commit
5ac7ae9c26
17 changed files with 1571 additions and 1127 deletions
6
.clang-format
Normal file
6
.clang-format
Normal file
|
@ -0,0 +1,6 @@
|
|||
---
|
||||
Language: Proto
|
||||
BasedOnStyle: Google
|
||||
AlignConsecutiveAssignments: true
|
||||
AlignConsecutiveDeclarations: true
|
||||
ColumnLimit: 0
|
|
@ -85,7 +85,7 @@ func Test_populateLogEvent(t *testing.T) {
|
|||
{log.AuthorizeLogFieldQuery, s, `{"query":"a=b"}`},
|
||||
{log.AuthorizeLogFieldRemovedGroupsCount, s, `{"removed-groups-count":42}`},
|
||||
{log.AuthorizeLogFieldRequestID, s, `{"request-id":"REQUEST-ID"}`},
|
||||
{log.AuthorizeLogFieldRouteChecksum, s, `{"route-checksum":14294378844626301341}`},
|
||||
{log.AuthorizeLogFieldRouteChecksum, s, `{"route-checksum":7416256365460802121}`},
|
||||
{log.AuthorizeLogFieldRouteID, s, `{"route-id":"POLICY-ID"}`},
|
||||
{log.AuthorizeLogFieldServiceAccountID, sa, `{"service-account-id":"SERVICE-ACCOUNT-ID"}`},
|
||||
{log.AuthorizeLogFieldSessionID, s, `{"session-id":"SESSION-ID"}`},
|
||||
|
|
56
config/circuit_breaker_thresholds.go
Normal file
56
config/circuit_breaker_thresholds.go
Normal file
|
@ -0,0 +1,56 @@
|
|||
package config
|
||||
|
||||
import (
|
||||
"github.com/volatiletech/null/v9"
|
||||
|
||||
configpb "github.com/pomerium/pomerium/pkg/grpc/config"
|
||||
)
|
||||
|
||||
// CircuitBreakerThresholds define thresholds for circuit breaking.
|
||||
type CircuitBreakerThresholds struct {
|
||||
MaxConnections null.Uint32 `mapstructure:"max_connections" yaml:"max_connections,omitempty" json:"max_connections,omitempty"`
|
||||
MaxPendingRequests null.Uint32 `mapstructure:"max_pending_requests" yaml:"max_pending_requests,omitempty" json:"max_pending_requests,omitempty"`
|
||||
MaxRequests null.Uint32 `mapstructure:"max_requests" yaml:"max_requests,omitempty" json:"max_requests,omitempty"`
|
||||
MaxRetries null.Uint32 `mapstructure:"max_retries" yaml:"max_retries,omitempty" json:"max_retries,omitempty"`
|
||||
MaxConnectionPools null.Uint32 `mapstructure:"max_connection_pools" yaml:"max_connection_pools,omitempty" json:"max_connection_pools,omitempty"`
|
||||
}
|
||||
|
||||
// CircuitBreakerThresholdsFromPB converts the CircuitBreakerThresholds from a protobuf type.
|
||||
func CircuitBreakerThresholdsFromPB(src *configpb.CircuitBreakerThresholds) *CircuitBreakerThresholds {
|
||||
if src == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
dst := &CircuitBreakerThresholds{}
|
||||
if src.MaxConnections != nil {
|
||||
dst.MaxConnections = null.Uint32From(*src.MaxConnections)
|
||||
}
|
||||
if src.MaxPendingRequests != nil {
|
||||
dst.MaxPendingRequests = null.Uint32From(*src.MaxPendingRequests)
|
||||
}
|
||||
if src.MaxRequests != nil {
|
||||
dst.MaxRequests = null.Uint32From(*src.MaxRequests)
|
||||
}
|
||||
if src.MaxRetries != nil {
|
||||
dst.MaxRetries = null.Uint32From(*src.MaxRetries)
|
||||
}
|
||||
if src.MaxConnectionPools != nil {
|
||||
dst.MaxConnectionPools = null.Uint32From(*src.MaxConnectionPools)
|
||||
}
|
||||
return dst
|
||||
}
|
||||
|
||||
// CircuitBreakerThresholdsToPB converts the CircuitBreakerThresholds into a protobuf type.
|
||||
func CircuitBreakerThresholdsToPB(src *CircuitBreakerThresholds) *configpb.CircuitBreakerThresholds {
|
||||
if src == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return &configpb.CircuitBreakerThresholds{
|
||||
MaxConnections: src.MaxConnections.Ptr(),
|
||||
MaxPendingRequests: src.MaxPendingRequests.Ptr(),
|
||||
MaxRequests: src.MaxRequests.Ptr(),
|
||||
MaxRetries: src.MaxRetries.Ptr(),
|
||||
MaxConnectionPools: src.MaxConnectionPools.Ptr(),
|
||||
}
|
||||
}
|
|
@ -37,6 +37,7 @@ var ViperPolicyHooks = viper.DecodeHook(mapstructure.ComposeDecodeHookFunc(
|
|||
// parse base-64 encoded POLICY that is bound to environment variable
|
||||
DecodePolicyBase64Hook(),
|
||||
decodeNullBoolHookFunc(),
|
||||
decodeNullUint32HookFunc(),
|
||||
decodeJWTClaimHeadersHookFunc(),
|
||||
decodeBearerTokenFormatHookFunc(),
|
||||
decodeCodecTypeHookFunc(),
|
||||
|
|
|
@ -46,6 +46,25 @@ func decodeNullBoolHookFunc() mapstructure.DecodeHookFunc {
|
|||
}
|
||||
}
|
||||
|
||||
func decodeNullUint32HookFunc() mapstructure.DecodeHookFunc {
|
||||
return func(_, t reflect.Type, data any) (any, error) {
|
||||
if t != reflect.TypeOf(null.Uint32{}) {
|
||||
return data, nil
|
||||
}
|
||||
|
||||
bs, err := json.Marshal(data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var value null.Uint32
|
||||
err = json.Unmarshal(bs, &value)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return value, nil
|
||||
}
|
||||
}
|
||||
|
||||
// JWTClaimHeaders are headers to add to a request based on IDP claims.
|
||||
type JWTClaimHeaders map[string]string
|
||||
|
||||
|
|
|
@ -260,6 +260,7 @@ func (b *Builder) BuildBootstrapStaticResources(
|
|||
},
|
||||
},
|
||||
TypedExtensionProtocolOptions: buildTypedExtensionProtocolOptions(nil, upstreamProtocolHTTP2, Keepalive(false)),
|
||||
CircuitBreakers: buildInternalCircuitBreakers(cfg),
|
||||
}
|
||||
|
||||
staticResources.Clusters = append(staticResources.Clusters, controlPlaneCluster)
|
||||
|
|
|
@ -71,6 +71,14 @@ func TestBuilder_BuildBootstrapStaticResources(t *testing.T) {
|
|||
"name": "pomerium-control-plane-grpc",
|
||||
"type": "STATIC",
|
||||
"connectTimeout": "5s",
|
||||
"circuitBreakers": {
|
||||
"thresholds": [{
|
||||
"maxConnectionPools": 4294967295,
|
||||
"maxConnections": 4294967295,
|
||||
"maxPendingRequests": 4294967295,
|
||||
"maxRequests": 4294967295
|
||||
}]
|
||||
},
|
||||
"loadAssignment": {
|
||||
"clusterName": "pomerium-control-plane-grpc",
|
||||
"endpoints": [{
|
||||
|
|
83
config/envoyconfig/circuit_breakers.go
Normal file
83
config/envoyconfig/circuit_breakers.go
Normal file
|
@ -0,0 +1,83 @@
|
|||
package envoyconfig
|
||||
|
||||
import (
|
||||
"math"
|
||||
|
||||
envoy_config_cluster_v3 "github.com/envoyproxy/go-control-plane/envoy/config/cluster/v3"
|
||||
envoy_config_core_v3 "github.com/envoyproxy/go-control-plane/envoy/config/core/v3"
|
||||
"google.golang.org/protobuf/proto"
|
||||
"google.golang.org/protobuf/types/known/wrapperspb"
|
||||
|
||||
"github.com/pomerium/pomerium/config"
|
||||
)
|
||||
|
||||
// unlimitedCircuitBreakersThreshold sets the circuit breaking thresholds to the maximum value, effectively disabling them
|
||||
var unlimitedCircuitBreakersThreshold = &envoy_config_cluster_v3.CircuitBreakers_Thresholds{
|
||||
Priority: envoy_config_core_v3.RoutingPriority_DEFAULT,
|
||||
MaxConnections: wrapperspb.UInt32(math.MaxUint32),
|
||||
MaxPendingRequests: wrapperspb.UInt32(math.MaxUint32),
|
||||
MaxRequests: wrapperspb.UInt32(math.MaxUint32),
|
||||
MaxConnectionPools: wrapperspb.UInt32(math.MaxUint32),
|
||||
}
|
||||
|
||||
func buildInternalCircuitBreakers(cfg *config.Config) *envoy_config_cluster_v3.CircuitBreakers {
|
||||
threshold := unlimitedCircuitBreakersThreshold
|
||||
if cfg != nil && cfg.Options != nil {
|
||||
threshold = buildCircuitBreakersThreshold(threshold, cfg.Options.CircuitBreakerThresholds)
|
||||
}
|
||||
if threshold == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return &envoy_config_cluster_v3.CircuitBreakers{
|
||||
Thresholds: []*envoy_config_cluster_v3.CircuitBreakers_Thresholds{threshold},
|
||||
}
|
||||
}
|
||||
|
||||
func buildRouteCircuitBreakers(cfg *config.Config, policy *config.Policy) *envoy_config_cluster_v3.CircuitBreakers {
|
||||
threshold := (*envoy_config_cluster_v3.CircuitBreakers_Thresholds)(nil)
|
||||
if cfg != nil && cfg.Options != nil {
|
||||
threshold = buildCircuitBreakersThreshold(threshold, cfg.Options.CircuitBreakerThresholds)
|
||||
}
|
||||
if policy != nil {
|
||||
threshold = buildCircuitBreakersThreshold(threshold, policy.CircuitBreakerThresholds)
|
||||
}
|
||||
|
||||
if threshold == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return &envoy_config_cluster_v3.CircuitBreakers{
|
||||
Thresholds: []*envoy_config_cluster_v3.CircuitBreakers_Thresholds{threshold},
|
||||
}
|
||||
}
|
||||
|
||||
func buildCircuitBreakersThreshold(dst *envoy_config_cluster_v3.CircuitBreakers_Thresholds, src *config.CircuitBreakerThresholds) *envoy_config_cluster_v3.CircuitBreakers_Thresholds {
|
||||
if src == nil {
|
||||
return dst
|
||||
}
|
||||
|
||||
if dst == nil {
|
||||
dst = new(envoy_config_cluster_v3.CircuitBreakers_Thresholds)
|
||||
} else {
|
||||
dst = proto.CloneOf(dst)
|
||||
}
|
||||
|
||||
if src.MaxConnections.IsSet() {
|
||||
dst.MaxConnections = wrapperspb.UInt32(src.MaxConnections.Uint32)
|
||||
}
|
||||
if src.MaxPendingRequests.IsSet() {
|
||||
dst.MaxPendingRequests = wrapperspb.UInt32(src.MaxPendingRequests.Uint32)
|
||||
}
|
||||
if src.MaxRequests.IsSet() {
|
||||
dst.MaxRequests = wrapperspb.UInt32(src.MaxRequests.Uint32)
|
||||
}
|
||||
if src.MaxRetries.IsSet() {
|
||||
dst.MaxRetries = wrapperspb.UInt32(src.MaxRetries.Uint32)
|
||||
}
|
||||
if src.MaxConnectionPools.IsSet() {
|
||||
dst.MaxConnectionPools = wrapperspb.UInt32(src.MaxConnectionPools.Uint32)
|
||||
}
|
||||
|
||||
return dst
|
||||
}
|
|
@ -152,6 +152,7 @@ func (b *Builder) buildInternalCluster(
|
|||
if err := b.buildCluster(cluster, name, endpoints, upstreamProtocol, keepalive); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cluster.CircuitBreakers = buildInternalCircuitBreakers(cfg)
|
||||
|
||||
return cluster, nil
|
||||
}
|
||||
|
@ -205,6 +206,7 @@ func (b *Builder) buildPolicyCluster(ctx context.Context, cfg *config.Config, po
|
|||
if err := b.buildCluster(cluster, name, endpoints, upstreamProtocol, Keepalive(false)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cluster.CircuitBreakers = buildRouteCircuitBreakers(cfg, policy)
|
||||
|
||||
return cluster, nil
|
||||
}
|
||||
|
|
|
@ -100,7 +100,7 @@ func TestBuilder_buildMainRouteConfiguration(t *testing.T) {
|
|||
"checkSettings": {
|
||||
"contextExtensions": {
|
||||
"internal": "false",
|
||||
"route_checksum": "4844561823473827050",
|
||||
"route_checksum": "3842393772597897044",
|
||||
"route_id": "5fbd81d8f19363f4"
|
||||
}
|
||||
}
|
||||
|
@ -158,7 +158,7 @@ func TestBuilder_buildMainRouteConfiguration(t *testing.T) {
|
|||
"checkSettings": {
|
||||
"contextExtensions": {
|
||||
"internal": "false",
|
||||
"route_checksum": "4844561823473827050",
|
||||
"route_checksum": "3842393772597897044",
|
||||
"route_id": "5fbd81d8f19363f4"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -405,14 +405,14 @@ func Test_buildPolicyRoutes(t *testing.T) {
|
|||
8: "301084c3bd94c1ed",
|
||||
}
|
||||
routeChecksums := []string{
|
||||
1: "1550825365439203068",
|
||||
2: "1743754064510868859",
|
||||
3: "5973469357905660470",
|
||||
4: "10629393024495644652",
|
||||
5: "17050190873730880526",
|
||||
6: "4829848755825381466",
|
||||
7: "7941222915300424536",
|
||||
8: "8084661313119959810",
|
||||
1: "12033848814082772143",
|
||||
2: "17258478527403939037",
|
||||
3: "8444406770556299357",
|
||||
4: "4105318140299980592",
|
||||
5: "10806704411058754499",
|
||||
6: "3851124977091699412",
|
||||
7: "4628039018923516807",
|
||||
8: "2191103304818138823",
|
||||
}
|
||||
|
||||
b := &Builder{filemgr: filemgr.NewManager(), reproxy: reproxy.New()}
|
||||
|
@ -1217,7 +1217,7 @@ func Test_buildPolicyRoutes(t *testing.T) {
|
|||
"checkSettings": {
|
||||
"contextExtensions": {
|
||||
"internal": "false",
|
||||
"route_checksum": "16194904053254305772",
|
||||
"route_checksum": "6606147546948811510",
|
||||
"route_id": "98f90d58022ca963"
|
||||
}
|
||||
}
|
||||
|
@ -1294,7 +1294,7 @@ func Test_buildPolicyRoutes(t *testing.T) {
|
|||
"checkSettings": {
|
||||
"contextExtensions": {
|
||||
"internal": "false",
|
||||
"route_checksum": "10734663134999137402",
|
||||
"route_checksum": "17040385240945756229",
|
||||
"route_id": "81175a3a9df11dd8"
|
||||
}
|
||||
}
|
||||
|
@ -1392,7 +1392,7 @@ func Test_buildPolicyRoutes(t *testing.T) {
|
|||
"checkSettings": {
|
||||
"contextExtensions": {
|
||||
"internal": "false",
|
||||
"route_checksum": "6431934433938620139",
|
||||
"route_checksum": "8569669228737044894",
|
||||
"route_id": "ad0a23467bbdb773"
|
||||
}
|
||||
}
|
||||
|
@ -1495,7 +1495,7 @@ func Test_buildPolicyRoutes(t *testing.T) {
|
|||
"checkSettings": {
|
||||
"contextExtensions": {
|
||||
"internal": "false",
|
||||
"route_checksum": "185312241489123079",
|
||||
"route_checksum": "17193711666127451236",
|
||||
"route_id": "1013c6be524d7fbd"
|
||||
}
|
||||
}
|
||||
|
@ -1611,7 +1611,7 @@ func Test_buildPolicyRoutes(t *testing.T) {
|
|||
"checkSettings": {
|
||||
"contextExtensions": {
|
||||
"internal": "false",
|
||||
"route_checksum": "10135716377738705841",
|
||||
"route_checksum": "5598645225002145886",
|
||||
"route_id": "a81e6b1e66c1e2cd"
|
||||
}
|
||||
}
|
||||
|
@ -1746,7 +1746,7 @@ func Test_buildPolicyRoutesRewrite(t *testing.T) {
|
|||
"checkSettings": {
|
||||
"contextExtensions": {
|
||||
"internal": "false",
|
||||
"route_checksum": "14883526649905267120",
|
||||
"route_checksum": "12006715121823499564",
|
||||
"route_id": "4d5ee69fcc359f45"
|
||||
}
|
||||
}
|
||||
|
@ -1822,7 +1822,7 @@ func Test_buildPolicyRoutesRewrite(t *testing.T) {
|
|||
"checkSettings": {
|
||||
"contextExtensions": {
|
||||
"internal": "false",
|
||||
"route_checksum": "10046758419081543299",
|
||||
"route_checksum": "16841768668336313196",
|
||||
"route_id": "4d5ee69fcc359f45"
|
||||
}
|
||||
}
|
||||
|
@ -1903,7 +1903,7 @@ func Test_buildPolicyRoutesRewrite(t *testing.T) {
|
|||
"checkSettings": {
|
||||
"contextExtensions": {
|
||||
"internal": "false",
|
||||
"route_checksum": "2754443979682419848",
|
||||
"route_checksum": "4748097581015346493",
|
||||
"route_id": "4d5ee69fcc359f45"
|
||||
}
|
||||
}
|
||||
|
@ -1979,7 +1979,7 @@ func Test_buildPolicyRoutesRewrite(t *testing.T) {
|
|||
"checkSettings": {
|
||||
"contextExtensions": {
|
||||
"internal": "false",
|
||||
"route_checksum": "1546259218106347577",
|
||||
"route_checksum": "17768398617143399322",
|
||||
"route_id": "4d5ee69fcc359f45"
|
||||
}
|
||||
}
|
||||
|
@ -2055,7 +2055,7 @@ func Test_buildPolicyRoutesRewrite(t *testing.T) {
|
|||
"checkSettings": {
|
||||
"contextExtensions": {
|
||||
"internal": "false",
|
||||
"route_checksum": "8023363204239692999",
|
||||
"route_checksum": "16285305135509499281",
|
||||
"route_id": "4d5ee69fcc359f45"
|
||||
}
|
||||
}
|
||||
|
@ -2136,7 +2136,7 @@ func Test_buildPolicyRoutesRewrite(t *testing.T) {
|
|||
"checkSettings": {
|
||||
"contextExtensions": {
|
||||
"internal": "false",
|
||||
"route_checksum": "5173412791633652167",
|
||||
"route_checksum": "5072687206675243600",
|
||||
"route_id": "4d5ee69fcc359f45"
|
||||
}
|
||||
}
|
||||
|
|
50
config/envoyconfig/testdata/clusters.json
vendored
50
config/envoyconfig/testdata/clusters.json
vendored
|
@ -23,6 +23,16 @@
|
|||
},
|
||||
{
|
||||
"connectTimeout": "10s",
|
||||
"circuitBreakers": {
|
||||
"thresholds": [
|
||||
{
|
||||
"maxConnectionPools": 4294967295,
|
||||
"maxConnections": 4294967295,
|
||||
"maxPendingRequests": 4294967295,
|
||||
"maxRequests": 4294967295
|
||||
}
|
||||
]
|
||||
},
|
||||
"dnsLookupFamily": "V4_PREFERRED",
|
||||
"loadAssignment": {
|
||||
"clusterName": "pomerium-control-plane-grpc",
|
||||
|
@ -71,6 +81,16 @@
|
|||
},
|
||||
{
|
||||
"connectTimeout": "10s",
|
||||
"circuitBreakers": {
|
||||
"thresholds": [
|
||||
{
|
||||
"maxConnectionPools": 4294967295,
|
||||
"maxConnections": 4294967295,
|
||||
"maxPendingRequests": 4294967295,
|
||||
"maxRequests": 4294967295
|
||||
}
|
||||
]
|
||||
},
|
||||
"dnsLookupFamily": "V4_PREFERRED",
|
||||
"loadAssignment": {
|
||||
"clusterName": "pomerium-control-plane-http",
|
||||
|
@ -123,6 +143,16 @@
|
|||
},
|
||||
{
|
||||
"connectTimeout": "10s",
|
||||
"circuitBreakers": {
|
||||
"thresholds": [
|
||||
{
|
||||
"maxConnectionPools": 4294967295,
|
||||
"maxConnections": 4294967295,
|
||||
"maxPendingRequests": 4294967295,
|
||||
"maxRequests": 4294967295
|
||||
}
|
||||
]
|
||||
},
|
||||
"dnsLookupFamily": "V4_PREFERRED",
|
||||
"loadAssignment": {
|
||||
"clusterName": "pomerium-control-plane-metrics",
|
||||
|
@ -175,6 +205,16 @@
|
|||
},
|
||||
{
|
||||
"connectTimeout": "10s",
|
||||
"circuitBreakers": {
|
||||
"thresholds": [
|
||||
{
|
||||
"maxConnectionPools": 4294967295,
|
||||
"maxConnections": 4294967295,
|
||||
"maxPendingRequests": 4294967295,
|
||||
"maxRequests": 4294967295
|
||||
}
|
||||
]
|
||||
},
|
||||
"dnsLookupFamily": "V4_PREFERRED",
|
||||
"loadAssignment": {
|
||||
"clusterName": "pomerium-authorize",
|
||||
|
@ -223,6 +263,16 @@
|
|||
},
|
||||
{
|
||||
"connectTimeout": "10s",
|
||||
"circuitBreakers": {
|
||||
"thresholds": [
|
||||
{
|
||||
"maxConnectionPools": 4294967295,
|
||||
"maxConnections": 4294967295,
|
||||
"maxPendingRequests": 4294967295,
|
||||
"maxRequests": 4294967295
|
||||
}
|
||||
]
|
||||
},
|
||||
"dnsLookupFamily": "V4_PREFERRED",
|
||||
"loadAssignment": {
|
||||
"clusterName": "pomerium-databroker",
|
||||
|
|
|
@ -292,6 +292,7 @@ type Options struct {
|
|||
RuntimeFlags RuntimeFlags `mapstructure:"runtime_flags" yaml:"runtime_flags,omitempty"`
|
||||
|
||||
HTTP3AdvertisePort null.Uint32 `mapstructure:"-" yaml:"-" json:"-"`
|
||||
CircuitBreakerThresholds *CircuitBreakerThresholds `mapstructure:"circuit_breaker_thresholds" yaml:"circuit_breaker_thresholds" json:"circuit_breaker_thresholds"`
|
||||
}
|
||||
|
||||
type certificateFilePair struct {
|
||||
|
@ -1593,6 +1594,9 @@ func (o *Options) ApplySettings(ctx context.Context, certsIndex *cryptutil.Certi
|
|||
return RuntimeFlag(k), v
|
||||
})
|
||||
o.HTTP3AdvertisePort = null.Uint32FromPtr(settings.Http3AdvertisePort)
|
||||
if settings.CircuitBreakerThresholds != nil {
|
||||
o.CircuitBreakerThresholds = CircuitBreakerThresholdsFromPB(settings.CircuitBreakerThresholds)
|
||||
}
|
||||
}
|
||||
|
||||
func (o *Options) ToProto() *config.Config {
|
||||
|
@ -1720,6 +1724,9 @@ func (o *Options) ToProto() *config.Config {
|
|||
return string(k), v
|
||||
})
|
||||
settings.Http3AdvertisePort = o.HTTP3AdvertisePort.Ptr()
|
||||
if o.CircuitBreakerThresholds != nil {
|
||||
settings.CircuitBreakerThresholds = CircuitBreakerThresholdsToPB(o.CircuitBreakerThresholds)
|
||||
}
|
||||
|
||||
routes := make([]*config.Route, 0, o.NumPolicies())
|
||||
for p := range o.GetAllPolicies() {
|
||||
|
|
|
@ -32,6 +32,7 @@ import (
|
|||
"github.com/spf13/viper"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/volatiletech/null/v9"
|
||||
"google.golang.org/protobuf/proto"
|
||||
"google.golang.org/protobuf/reflect/protoreflect"
|
||||
"google.golang.org/protobuf/types/known/fieldmaskpb"
|
||||
|
@ -1035,6 +1036,21 @@ func TestOptions_ApplySettings(t *testing.T) {
|
|||
assert.Equal(t, ptr([]string{"x", "y", "z"}), options.IDPAccessTokenAllowedAudiences,
|
||||
"should preserve idp access token allowed audiences")
|
||||
})
|
||||
|
||||
t.Run("circuit_breaker_thresholds", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
options := NewDefaultOptions()
|
||||
assert.Nil(t, options.CircuitBreakerThresholds)
|
||||
options.ApplySettings(ctx, nil, &configpb.Settings{
|
||||
CircuitBreakerThresholds: &configpb.CircuitBreakerThresholds{
|
||||
MaxConnections: proto.Uint32(3),
|
||||
},
|
||||
})
|
||||
assert.Equal(t, &CircuitBreakerThresholds{MaxConnections: null.Uint32From(3)}, options.CircuitBreakerThresholds)
|
||||
options.ApplySettings(ctx, nil, &configpb.Settings{})
|
||||
assert.Equal(t, &CircuitBreakerThresholds{MaxConnections: null.Uint32From(3)}, options.CircuitBreakerThresholds,
|
||||
"should not erase existing circuit breaker thresholds")
|
||||
})
|
||||
}
|
||||
|
||||
func TestOptions_GetSetResponseHeaders(t *testing.T) {
|
||||
|
|
|
@ -207,6 +207,7 @@ type Policy struct {
|
|||
|
||||
// MCP is an experimental support for Model Context Protocol upstreams
|
||||
MCP *MCP `mapstructure:"mcp" yaml:"mcp,omitempty" json:"mcp,omitempty"`
|
||||
CircuitBreakerThresholds *CircuitBreakerThresholds `mapstructure:"circuit_breaker_thresholds" yaml:"circuit_breaker_thresholds,omitempty" json:"circuit_breaker_thresholds,omitempty"`
|
||||
}
|
||||
|
||||
// MCP is an experimental support for Model Context Protocol upstreams configuration
|
||||
|
@ -345,6 +346,7 @@ func NewPolicyFromProto(pb *configpb.Route) (*Policy, error) {
|
|||
AllowPublicUnauthenticatedAccess: pb.GetAllowPublicUnauthenticatedAccess(),
|
||||
AllowSPDY: pb.GetAllowSpdy(),
|
||||
AllowWebsockets: pb.GetAllowWebsockets(),
|
||||
CircuitBreakerThresholds: CircuitBreakerThresholdsFromPB(pb.CircuitBreakerThresholds),
|
||||
CORSAllowPreflight: pb.GetCorsAllowPreflight(),
|
||||
Description: pb.GetDescription(),
|
||||
DependsOn: pb.GetDependsOn(),
|
||||
|
@ -502,6 +504,7 @@ func (p *Policy) ToProto() (*configpb.Route, error) {
|
|||
AllowPublicUnauthenticatedAccess: p.AllowPublicUnauthenticatedAccess,
|
||||
AllowSpdy: p.AllowSPDY,
|
||||
AllowWebsockets: p.AllowWebsockets,
|
||||
CircuitBreakerThresholds: CircuitBreakerThresholdsToPB(p.CircuitBreakerThresholds),
|
||||
CorsAllowPreflight: p.CORSAllowPreflight,
|
||||
Description: p.Description,
|
||||
DependsOn: p.DependsOn,
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -55,7 +55,29 @@ enum BearerTokenFormat {
|
|||
BEARER_TOKEN_FORMAT_IDP_IDENTITY_TOKEN = 3;
|
||||
}
|
||||
|
||||
// Next ID: 73.
|
||||
// CircuitBreakerThresholds defines CircuitBreaker settings.
|
||||
message CircuitBreakerThresholds {
|
||||
// The maximum number of connections that Envoy will make to the upstream
|
||||
// cluster. If not specified, the default is 1024.
|
||||
optional uint32 max_connections = 1;
|
||||
// The maximum number of pending requests that Envoy will allow to the
|
||||
// upstream cluster. If not specified, the default is 1024. This limit is
|
||||
// applied as a connection limit for non-HTTP traffic.
|
||||
optional uint32 max_pending_requests = 2;
|
||||
// The maximum number of parallel requests that Envoy will make to the
|
||||
// upstream cluster. If not specified, the default is 1024. This limit does
|
||||
// not apply to non-HTTP traffic.
|
||||
optional uint32 max_requests = 3;
|
||||
// The maximum number of parallel retries that Envoy will allow to the
|
||||
// upstream cluster. If not specified, the default is 3.
|
||||
optional uint32 max_retries = 4;
|
||||
// The maximum number of connection pools per cluster that Envoy will
|
||||
// concurrently support at once. If not specified, the default is unlimited.
|
||||
// Set this for clusters which create a large number of connection pools.
|
||||
optional uint32 max_connection_pools = 5;
|
||||
}
|
||||
|
||||
// Next ID: 74.
|
||||
message Route {
|
||||
message StringList {
|
||||
repeated string values = 1;
|
||||
|
@ -149,6 +171,7 @@ message Route {
|
|||
bool show_error_details = 59;
|
||||
|
||||
optional MCP mcp = 72;
|
||||
optional CircuitBreakerThresholds circuit_breaker_thresholds = 73;
|
||||
}
|
||||
|
||||
message MCP {
|
||||
|
@ -202,7 +225,7 @@ message Policy {
|
|||
string remediation = 9;
|
||||
}
|
||||
|
||||
// Next ID: 140.
|
||||
// Next ID: 141.
|
||||
message Settings {
|
||||
message Certificate {
|
||||
bytes cert_bytes = 3;
|
||||
|
@ -314,7 +337,9 @@ message Settings {
|
|||
optional string envoy_bind_config_source_address = 111;
|
||||
optional bool envoy_bind_config_freebind = 112;
|
||||
repeated string programmatic_redirect_domain_whitelist = 68;
|
||||
|
||||
optional envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager.CodecType codec_type = 73;
|
||||
|
||||
// optional pomerium.crypt.PublicKeyEncryptionKey audit_key = 72;
|
||||
optional string primary_color = 85;
|
||||
optional string secondary_color = 86;
|
||||
|
@ -326,6 +351,7 @@ message Settings {
|
|||
optional bool pass_identity_headers = 117;
|
||||
map<string, bool> runtime_flags = 118;
|
||||
optional uint32 http3_advertise_port = 136;
|
||||
optional CircuitBreakerThresholds circuit_breaker_thresholds = 140;
|
||||
}
|
||||
|
||||
message DownstreamMtlsSettings {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue