feat(ingress-gateway): support outlier detection of upstream service for ingress gateway (#15614)

* feat(ingress-gateway): support outlier detection of upstream service for ingress gateway

* changelog

Co-authored-by: Eric Haberkorn <erichaberkorn@gmail.com>
This commit is contained in:
cskh 2022-12-13 11:51:37 -05:00 committed by GitHub
parent 50a5549f8a
commit 3e37a449c8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
36 changed files with 1124 additions and 496 deletions

7
.changelog/15614.txt Normal file
View File

@ -0,0 +1,7 @@
```release-note:feature
config-entry(ingress-gateway): support outlier detection (passive health check) for upstream cluster
```
```release-note:breaking-change
ingress-gateway: upstream cluster will have empty outlier_detection if passive health check is unspecified
```

View File

@ -317,6 +317,14 @@ func (o *configSnapshotIngressGateway) DeepCopy() *configSnapshotIngressGateway
cp.Listeners[k2] = cp_Listeners_v2 cp.Listeners[k2] = cp_Listeners_v2
} }
} }
if o.Defaults.PassiveHealthCheck != nil {
cp.Defaults.PassiveHealthCheck = new(structs.PassiveHealthCheck)
*cp.Defaults.PassiveHealthCheck = *o.Defaults.PassiveHealthCheck
if o.Defaults.PassiveHealthCheck.EnforcingConsecutive5xx != nil {
cp.Defaults.PassiveHealthCheck.EnforcingConsecutive5xx = new(uint32)
*cp.Defaults.PassiveHealthCheck.EnforcingConsecutive5xx = *o.Defaults.PassiveHealthCheck.EnforcingConsecutive5xx
}
}
return &cp return &cp
} }

View File

@ -43,6 +43,10 @@ type IngressServiceConfig struct {
MaxConnections uint32 MaxConnections uint32
MaxPendingRequests uint32 MaxPendingRequests uint32
MaxConcurrentRequests uint32 MaxConcurrentRequests uint32
// PassiveHealthCheck configuration determines how upstream proxy instances will
// be monitored for removal from the load balancing pool.
PassiveHealthCheck *PassiveHealthCheck `json:",omitempty" alias:"passive_health_check"`
} }
type IngressListener struct { type IngressListener struct {
@ -103,6 +107,10 @@ type IngressService struct {
MaxPendingRequests uint32 `json:",omitempty" alias:"max_pending_requests"` MaxPendingRequests uint32 `json:",omitempty" alias:"max_pending_requests"`
MaxConcurrentRequests uint32 `json:",omitempty" alias:"max_concurrent_requests"` MaxConcurrentRequests uint32 `json:",omitempty" alias:"max_concurrent_requests"`
// PassiveHealthCheck configuration determines how upstream proxy instances will
// be monitored for removal from the load balancing pool.
PassiveHealthCheck *PassiveHealthCheck `json:",omitempty" alias:"passive_health_check"`
Meta map[string]string `json:",omitempty"` Meta map[string]string `json:",omitempty"`
acl.EnterpriseMeta `hcl:",squash" mapstructure:",squash"` acl.EnterpriseMeta `hcl:",squash" mapstructure:",squash"`
} }

View File

@ -325,6 +325,14 @@ func (o *IngressListener) DeepCopy() *IngressListener {
if o.Services[i2].ResponseHeaders != nil { if o.Services[i2].ResponseHeaders != nil {
cp.Services[i2].ResponseHeaders = o.Services[i2].ResponseHeaders.DeepCopy() cp.Services[i2].ResponseHeaders = o.Services[i2].ResponseHeaders.DeepCopy()
} }
if o.Services[i2].PassiveHealthCheck != nil {
cp.Services[i2].PassiveHealthCheck = new(PassiveHealthCheck)
*cp.Services[i2].PassiveHealthCheck = *o.Services[i2].PassiveHealthCheck
if o.Services[i2].PassiveHealthCheck.EnforcingConsecutive5xx != nil {
cp.Services[i2].PassiveHealthCheck.EnforcingConsecutive5xx = new(uint32)
*cp.Services[i2].PassiveHealthCheck.EnforcingConsecutive5xx = *o.Services[i2].PassiveHealthCheck.EnforcingConsecutive5xx
}
}
if o.Services[i2].Meta != nil { if o.Services[i2].Meta != nil {
cp.Services[i2].Meta = make(map[string]string, len(o.Services[i2].Meta)) cp.Services[i2].Meta = make(map[string]string, len(o.Services[i2].Meta))
for k4, v4 := range o.Services[i2].Meta { for k4, v4 := range o.Services[i2].Meta {

View File

@ -849,6 +849,20 @@ func (s *ResourceGenerator) configIngressUpstreamCluster(c *envoy_cluster_v3.Clu
if threshold != nil { if threshold != nil {
c.CircuitBreakers.Thresholds = []*envoy_cluster_v3.CircuitBreakers_Thresholds{threshold} c.CircuitBreakers.Thresholds = []*envoy_cluster_v3.CircuitBreakers_Thresholds{threshold}
} }
// Configure the outlier detector for upstream service
var override *structs.PassiveHealthCheck
if svc != nil {
override = svc.PassiveHealthCheck
}
outlierDetection := ToOutlierDetection(cfgSnap.IngressGateway.Defaults.PassiveHealthCheck, override, false)
// Specail handling for failover peering service, which has set MaxEjectionPercent
if c.OutlierDetection != nil && c.OutlierDetection.MaxEjectionPercent != nil {
outlierDetection.MaxEjectionPercent = &wrappers.UInt32Value{Value: c.OutlierDetection.MaxEjectionPercent.Value}
}
c.OutlierDetection = outlierDetection
} }
func (s *ResourceGenerator) makeAppCluster(cfgSnap *proxycfg.ConfigSnapshot, name, pathProtocol string, port int) (*envoy_cluster_v3.Cluster, error) { func (s *ResourceGenerator) makeAppCluster(cfgSnap *proxycfg.ConfigSnapshot, name, pathProtocol string, port int) (*envoy_cluster_v3.Cluster, error) {
@ -949,7 +963,7 @@ func (s *ResourceGenerator) makeUpstreamClusterForPeerService(
clusterName := generatePeeredClusterName(uid, tbs) clusterName := generatePeeredClusterName(uid, tbs)
outlierDetection := ToOutlierDetection(upstreamConfig.PassiveHealthCheck) outlierDetection := ToOutlierDetection(upstreamConfig.PassiveHealthCheck, nil, true)
// We can't rely on health checks for services on cluster peers because they // We can't rely on health checks for services on cluster peers because they
// don't take into account service resolvers, splitters and routers. Setting // don't take into account service resolvers, splitters and routers. Setting
// MaxEjectionPercent too 100% gives outlier detection the power to eject the // MaxEjectionPercent too 100% gives outlier detection the power to eject the
@ -1084,7 +1098,7 @@ func (s *ResourceGenerator) makeUpstreamClusterForPreparedQuery(upstream structs
CircuitBreakers: &envoy_cluster_v3.CircuitBreakers{ CircuitBreakers: &envoy_cluster_v3.CircuitBreakers{
Thresholds: makeThresholdsIfNeeded(cfg.Limits), Thresholds: makeThresholdsIfNeeded(cfg.Limits),
}, },
OutlierDetection: ToOutlierDetection(cfg.PassiveHealthCheck), OutlierDetection: ToOutlierDetection(cfg.PassiveHealthCheck, nil, true),
} }
if cfg.Protocol == "http2" || cfg.Protocol == "grpc" { if cfg.Protocol == "http2" || cfg.Protocol == "grpc" {
if err := s.setHttp2ProtocolOptions(c); err != nil { if err := s.setHttp2ProtocolOptions(c); err != nil {
@ -1327,7 +1341,7 @@ func (s *ResourceGenerator) makeUpstreamClustersForDiscoveryChain(
CircuitBreakers: &envoy_cluster_v3.CircuitBreakers{ CircuitBreakers: &envoy_cluster_v3.CircuitBreakers{
Thresholds: makeThresholdsIfNeeded(upstreamConfig.Limits), Thresholds: makeThresholdsIfNeeded(upstreamConfig.Limits),
}, },
OutlierDetection: ToOutlierDetection(upstreamConfig.PassiveHealthCheck), OutlierDetection: ToOutlierDetection(upstreamConfig.PassiveHealthCheck, nil, true),
} }
var lb *structs.LoadBalancer var lb *structs.LoadBalancer

View File

@ -549,6 +549,65 @@ func TestClustersFromSnapshot(t *testing.T) {
}, nil) }, nil)
}, },
}, },
{
name: "ingress-with-service-passive-health-check",
create: func(t testinf.T) *proxycfg.ConfigSnapshot {
return proxycfg.TestConfigSnapshotIngressGateway(t, true, "tcp",
"simple", nil,
func(entry *structs.IngressGatewayConfigEntry) {
entry.Listeners[0].Services[0].MaxConnections = 4096
entry.Listeners[0].Services[0].PassiveHealthCheck = &structs.PassiveHealthCheck{
Interval: 5000000000,
MaxFailures: 10,
}
}, nil)
},
},
{
name: "ingress-with-defaults-passive-health-check",
create: func(t testinf.T) *proxycfg.ConfigSnapshot {
return proxycfg.TestConfigSnapshotIngressGateway(t, true, "tcp",
"simple", nil,
func(entry *structs.IngressGatewayConfigEntry) {
enforcingConsecutive5xx := uint32(80)
entry.Defaults = &structs.IngressServiceConfig{
MaxConnections: 2048,
MaxPendingRequests: 512,
MaxConcurrentRequests: 4096,
PassiveHealthCheck: &structs.PassiveHealthCheck{
Interval: 5000000000,
MaxFailures: 10,
EnforcingConsecutive5xx: &enforcingConsecutive5xx,
},
}
}, nil)
},
},
{
name: "ingress-with-overwrite-defaults-passive-health-check",
create: func(t testinf.T) *proxycfg.ConfigSnapshot {
return proxycfg.TestConfigSnapshotIngressGateway(t, true, "tcp",
"simple", nil,
func(entry *structs.IngressGatewayConfigEntry) {
defaultEnforcingConsecutive5xx := uint32(80)
entry.Defaults = &structs.IngressServiceConfig{
MaxConnections: 2048,
MaxPendingRequests: 512,
PassiveHealthCheck: &structs.PassiveHealthCheck{
Interval: 5000000000,
EnforcingConsecutive5xx: &defaultEnforcingConsecutive5xx,
},
}
enforcingConsecutive5xx := uint32(50)
entry.Listeners[0].Services[0].MaxConnections = 4096
entry.Listeners[0].Services[0].MaxPendingRequests = 2048
entry.Listeners[0].Services[0].PassiveHealthCheck = &structs.PassiveHealthCheck{
Interval: 8000000000,
EnforcingConsecutive5xx: &enforcingConsecutive5xx,
}
}, nil)
},
},
{ {
name: "ingress-with-chain-external-sni", name: "ingress-with-chain-external-sni",
create: func(t testinf.T) *proxycfg.ConfigSnapshot { create: func(t testinf.T) *proxycfg.ConfigSnapshot {

View File

@ -175,24 +175,53 @@ func ParseGatewayConfig(m map[string]interface{}) (GatewayConfig, error) {
return cfg, err return cfg, err
} }
// Return an envoy.OutlierDetection populated by the values from this struct. // Return an envoy.OutlierDetection populated by the values from structs.PassiveHealthChec.
// If all values are zero a default empty OutlierDetection will be returned to // If all values are zero a default empty OutlierDetection will be returned to
// enable outlier detection with default values. // enable outlier detection with default values.
func ToOutlierDetection(p *structs.PassiveHealthCheck) *envoy_cluster_v3.OutlierDetection { // - If override is not nil, it will overwrite the values from p, e.g., ingress gateway defaults
// - allowZero is added to handle the legacy case where connect-proxy and mesh gateway can set 0
// for EnforcingConsecutive5xx. Due to the definition of proto of PassiveHealthCheck, ingress
// gateway's EnforcingConsecutive5xx must be > 0.
func ToOutlierDetection(p *structs.PassiveHealthCheck, override *structs.PassiveHealthCheck, allowZero bool) *envoy_cluster_v3.OutlierDetection {
od := &envoy_cluster_v3.OutlierDetection{} od := &envoy_cluster_v3.OutlierDetection{}
if p == nil { if p != nil {
if p.Interval != 0 {
od.Interval = durationpb.New(p.Interval)
}
if p.MaxFailures != 0 {
od.Consecutive_5Xx = &wrappers.UInt32Value{Value: p.MaxFailures}
}
if p.EnforcingConsecutive5xx != nil {
// NOTE: EnforcingConsecutive5xx must be great than 0 for ingress-gateway
if *p.EnforcingConsecutive5xx != 0 {
od.EnforcingConsecutive_5Xx = &wrappers.UInt32Value{Value: *p.EnforcingConsecutive5xx}
} else if allowZero {
od.EnforcingConsecutive_5Xx = &wrappers.UInt32Value{Value: *p.EnforcingConsecutive5xx}
}
}
}
if override == nil {
return od return od
} }
if p.Interval != 0 { // override the default outlier detection value
od.Interval = durationpb.New(p.Interval) if override.Interval != 0 {
od.Interval = durationpb.New(override.Interval)
} }
if p.MaxFailures != 0 { if override.MaxFailures != 0 {
od.Consecutive_5Xx = &wrappers.UInt32Value{Value: p.MaxFailures} od.Consecutive_5Xx = &wrappers.UInt32Value{Value: override.MaxFailures}
} }
if p.EnforcingConsecutive5xx != nil { if override.EnforcingConsecutive5xx != nil {
od.EnforcingConsecutive_5Xx = &wrappers.UInt32Value{Value: *p.EnforcingConsecutive5xx} // NOTE: EnforcingConsecutive5xx must be great than 0 for ingress-gateway
if *override.EnforcingConsecutive5xx != 0 {
od.EnforcingConsecutive_5Xx = &wrappers.UInt32Value{Value: *override.EnforcingConsecutive5xx}
} else if allowZero {
od.EnforcingConsecutive_5Xx = &wrappers.UInt32Value{Value: *override.EnforcingConsecutive5xx}
}
} }
return od return od

View File

@ -16,6 +16,9 @@
} }
}, },
"connectTimeout": "33s", "connectTimeout": "33s",
"outlierDetection": {
},
"lbPolicy": "CLUSTER_PROVIDED" "lbPolicy": "CLUSTER_PROVIDED"
}, },
{ {

View File

@ -16,6 +16,9 @@
} }
}, },
"connectTimeout": "33s", "connectTimeout": "33s",
"outlierDetection": {
},
"lbPolicy": "CLUSTER_PROVIDED" "lbPolicy": "CLUSTER_PROVIDED"
}, },
{ {

View File

@ -0,0 +1,73 @@
{
"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",
"type": "EDS",
"edsClusterConfig": {
"edsConfig": {
"ads": {
},
"resourceApiVersion": "V3"
}
},
"connectTimeout": "33s",
"circuitBreakers": {
"thresholds": [
{
"maxConnections": 2048,
"maxPendingRequests": 512,
"maxRequests": 4096
}
]
},
"outlierDetection": {
"consecutive5xx": 10,
"interval": "5s",
"enforcingConsecutive5xx": 80
},
"commonLbConfig": {
"healthyPanicThreshold": {
}
},
"transportSocket": {
"name": "tls",
"typedConfig": {
"@type": "type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext",
"commonTlsContext": {
"tlsParams": {
},
"tlsCertificates": [
{
"certificateChain": {
"inlineString": "-----BEGIN CERTIFICATE-----\nMIICjDCCAjKgAwIBAgIIC5llxGV1gB8wCgYIKoZIzj0EAwIwFDESMBAGA1UEAxMJ\nVGVzdCBDQSAyMB4XDTE5MDMyMjEzNTgyNloXDTI5MDMyMjEzNTgyNlowDjEMMAoG\nA1UEAxMDd2ViMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEADPv1RHVNRfa2VKR\nAB16b6rZnEt7tuhaxCFpQXPj7M2omb0B9Favq5E0ivpNtv1QnFhxtPd7d5k4e+T7\nSkW1TaOCAXIwggFuMA4GA1UdDwEB/wQEAwIDuDAdBgNVHSUEFjAUBggrBgEFBQcD\nAgYIKwYBBQUHAwEwDAYDVR0TAQH/BAIwADBoBgNVHQ4EYQRfN2Q6MDc6ODc6M2E6\nNDA6MTk6NDc6YzM6NWE6YzA6YmE6NjI6ZGY6YWY6NGI6ZDQ6MDU6MjU6NzY6M2Q6\nNWE6OGQ6MTY6OGQ6Njc6NWU6MmU6YTA6MzQ6N2Q6ZGM6ZmYwagYDVR0jBGMwYYBf\nZDE6MTE6MTE6YWM6MmE6YmE6OTc6YjI6M2Y6YWM6N2I6YmQ6ZGE6YmU6YjE6OGE6\nZmM6OWE6YmE6YjU6YmM6ODM6ZTc6NWU6NDE6NmY6ZjI6NzM6OTU6NTg6MGM6ZGIw\nWQYDVR0RBFIwUIZOc3BpZmZlOi8vMTExMTExMTEtMjIyMi0zMzMzLTQ0NDQtNTU1\nNTU1NTU1NTU1LmNvbnN1bC9ucy9kZWZhdWx0L2RjL2RjMS9zdmMvd2ViMAoGCCqG\nSM49BAMCA0gAMEUCIGC3TTvvjj76KMrguVyFf4tjOqaSCRie3nmHMRNNRav7AiEA\npY0heYeK9A6iOLrzqxSerkXXQyj5e9bE4VgUnxgPU6g=\n-----END CERTIFICATE-----\n"
},
"privateKey": {
"inlineString": "-----BEGIN EC PRIVATE KEY-----\nMHcCAQEEIMoTkpRggp3fqZzFKh82yS4LjtJI+XY+qX/7DefHFrtdoAoGCCqGSM49\nAwEHoUQDQgAEADPv1RHVNRfa2VKRAB16b6rZnEt7tuhaxCFpQXPj7M2omb0B9Fav\nq5E0ivpNtv1QnFhxtPd7d5k4e+T7SkW1TQ==\n-----END EC PRIVATE KEY-----\n"
}
}
],
"validationContext": {
"trustedCa": {
"inlineString": "-----BEGIN CERTIFICATE-----\nMIICXDCCAgKgAwIBAgIICpZq70Z9LyUwCgYIKoZIzj0EAwIwFDESMBAGA1UEAxMJ\nVGVzdCBDQSAyMB4XDTE5MDMyMjEzNTgyNloXDTI5MDMyMjEzNTgyNlowFDESMBAG\nA1UEAxMJVGVzdCBDQSAyMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEIhywH1gx\nAsMwuF3ukAI5YL2jFxH6Usnma1HFSfVyxbXX1/uoZEYrj8yCAtdU2yoHETyd+Zx2\nThhRLP79pYegCaOCATwwggE4MA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTAD\nAQH/MGgGA1UdDgRhBF9kMToxMToxMTphYzoyYTpiYTo5NzpiMjozZjphYzo3Yjpi\nZDpkYTpiZTpiMTo4YTpmYzo5YTpiYTpiNTpiYzo4MzplNzo1ZTo0MTo2ZjpmMjo3\nMzo5NTo1ODowYzpkYjBqBgNVHSMEYzBhgF9kMToxMToxMTphYzoyYTpiYTo5Nzpi\nMjozZjphYzo3YjpiZDpkYTpiZTpiMTo4YTpmYzo5YTpiYTpiNTpiYzo4MzplNzo1\nZTo0MTo2ZjpmMjo3Mzo5NTo1ODowYzpkYjA/BgNVHREEODA2hjRzcGlmZmU6Ly8x\nMTExMTExMS0yMjIyLTMzMzMtNDQ0NC01NTU1NTU1NTU1NTUuY29uc3VsMAoGCCqG\nSM49BAMCA0gAMEUCICOY0i246rQHJt8o8Oya0D5PLL1FnmsQmQqIGCi31RwnAiEA\noR5f6Ku+cig2Il8T8LJujOp2/2A72QcHZA57B13y+8o=\n-----END CERTIFICATE-----\n"
},
"matchSubjectAltNames": [
{
"exact": "spiffe://11111111-2222-3333-4444-555555555555.consul/ns/default/dc/dc1/svc/db"
}
]
}
},
"sni": "db.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul"
}
}
}
],
"typeUrl": "type.googleapis.com/envoy.config.cluster.v3.Cluster",
"nonce": "00000001"
}

View File

@ -0,0 +1,71 @@
{
"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",
"type": "EDS",
"edsClusterConfig": {
"edsConfig": {
"ads": {
},
"resourceApiVersion": "V3"
}
},
"connectTimeout": "33s",
"circuitBreakers": {
"thresholds": [
{
"maxConnections": 4096,
"maxPendingRequests": 2048
}
]
},
"outlierDetection": {
"interval": "8s",
"enforcingConsecutive5xx": 50
},
"commonLbConfig": {
"healthyPanicThreshold": {
}
},
"transportSocket": {
"name": "tls",
"typedConfig": {
"@type": "type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext",
"commonTlsContext": {
"tlsParams": {
},
"tlsCertificates": [
{
"certificateChain": {
"inlineString": "-----BEGIN CERTIFICATE-----\nMIICjDCCAjKgAwIBAgIIC5llxGV1gB8wCgYIKoZIzj0EAwIwFDESMBAGA1UEAxMJ\nVGVzdCBDQSAyMB4XDTE5MDMyMjEzNTgyNloXDTI5MDMyMjEzNTgyNlowDjEMMAoG\nA1UEAxMDd2ViMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEADPv1RHVNRfa2VKR\nAB16b6rZnEt7tuhaxCFpQXPj7M2omb0B9Favq5E0ivpNtv1QnFhxtPd7d5k4e+T7\nSkW1TaOCAXIwggFuMA4GA1UdDwEB/wQEAwIDuDAdBgNVHSUEFjAUBggrBgEFBQcD\nAgYIKwYBBQUHAwEwDAYDVR0TAQH/BAIwADBoBgNVHQ4EYQRfN2Q6MDc6ODc6M2E6\nNDA6MTk6NDc6YzM6NWE6YzA6YmE6NjI6ZGY6YWY6NGI6ZDQ6MDU6MjU6NzY6M2Q6\nNWE6OGQ6MTY6OGQ6Njc6NWU6MmU6YTA6MzQ6N2Q6ZGM6ZmYwagYDVR0jBGMwYYBf\nZDE6MTE6MTE6YWM6MmE6YmE6OTc6YjI6M2Y6YWM6N2I6YmQ6ZGE6YmU6YjE6OGE6\nZmM6OWE6YmE6YjU6YmM6ODM6ZTc6NWU6NDE6NmY6ZjI6NzM6OTU6NTg6MGM6ZGIw\nWQYDVR0RBFIwUIZOc3BpZmZlOi8vMTExMTExMTEtMjIyMi0zMzMzLTQ0NDQtNTU1\nNTU1NTU1NTU1LmNvbnN1bC9ucy9kZWZhdWx0L2RjL2RjMS9zdmMvd2ViMAoGCCqG\nSM49BAMCA0gAMEUCIGC3TTvvjj76KMrguVyFf4tjOqaSCRie3nmHMRNNRav7AiEA\npY0heYeK9A6iOLrzqxSerkXXQyj5e9bE4VgUnxgPU6g=\n-----END CERTIFICATE-----\n"
},
"privateKey": {
"inlineString": "-----BEGIN EC PRIVATE KEY-----\nMHcCAQEEIMoTkpRggp3fqZzFKh82yS4LjtJI+XY+qX/7DefHFrtdoAoGCCqGSM49\nAwEHoUQDQgAEADPv1RHVNRfa2VKRAB16b6rZnEt7tuhaxCFpQXPj7M2omb0B9Fav\nq5E0ivpNtv1QnFhxtPd7d5k4e+T7SkW1TQ==\n-----END EC PRIVATE KEY-----\n"
}
}
],
"validationContext": {
"trustedCa": {
"inlineString": "-----BEGIN CERTIFICATE-----\nMIICXDCCAgKgAwIBAgIICpZq70Z9LyUwCgYIKoZIzj0EAwIwFDESMBAGA1UEAxMJ\nVGVzdCBDQSAyMB4XDTE5MDMyMjEzNTgyNloXDTI5MDMyMjEzNTgyNlowFDESMBAG\nA1UEAxMJVGVzdCBDQSAyMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEIhywH1gx\nAsMwuF3ukAI5YL2jFxH6Usnma1HFSfVyxbXX1/uoZEYrj8yCAtdU2yoHETyd+Zx2\nThhRLP79pYegCaOCATwwggE4MA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTAD\nAQH/MGgGA1UdDgRhBF9kMToxMToxMTphYzoyYTpiYTo5NzpiMjozZjphYzo3Yjpi\nZDpkYTpiZTpiMTo4YTpmYzo5YTpiYTpiNTpiYzo4MzplNzo1ZTo0MTo2ZjpmMjo3\nMzo5NTo1ODowYzpkYjBqBgNVHSMEYzBhgF9kMToxMToxMTphYzoyYTpiYTo5Nzpi\nMjozZjphYzo3YjpiZDpkYTpiZTpiMTo4YTpmYzo5YTpiYTpiNTpiYzo4MzplNzo1\nZTo0MTo2ZjpmMjo3Mzo5NTo1ODowYzpkYjA/BgNVHREEODA2hjRzcGlmZmU6Ly8x\nMTExMTExMS0yMjIyLTMzMzMtNDQ0NC01NTU1NTU1NTU1NTUuY29uc3VsMAoGCCqG\nSM49BAMCA0gAMEUCICOY0i246rQHJt8o8Oya0D5PLL1FnmsQmQqIGCi31RwnAiEA\noR5f6Ku+cig2Il8T8LJujOp2/2A72QcHZA57B13y+8o=\n-----END CERTIFICATE-----\n"
},
"matchSubjectAltNames": [
{
"exact": "spiffe://11111111-2222-3333-4444-555555555555.consul/ns/default/dc/dc1/svc/db"
}
]
}
},
"sni": "db.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul"
}
}
}
],
"typeUrl": "type.googleapis.com/envoy.config.cluster.v3.Cluster",
"nonce": "00000001"
}

View File

@ -0,0 +1,71 @@
{
"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",
"type": "EDS",
"edsClusterConfig": {
"edsConfig": {
"ads": {
},
"resourceApiVersion": "V3"
}
},
"connectTimeout": "33s",
"circuitBreakers": {
"thresholds": [
{
"maxConnections": 4096,
"maxPendingRequests": 2048
}
]
},
"outlierDetection": {
"interval": "8s",
"enforcingConsecutive5xx": 50
},
"commonLbConfig": {
"healthyPanicThreshold": {
}
},
"transportSocket": {
"name": "tls",
"typedConfig": {
"@type": "type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext",
"commonTlsContext": {
"tlsParams": {
},
"tlsCertificates": [
{
"certificateChain": {
"inlineString": "-----BEGIN CERTIFICATE-----\nMIICjDCCAjKgAwIBAgIIC5llxGV1gB8wCgYIKoZIzj0EAwIwFDESMBAGA1UEAxMJ\nVGVzdCBDQSAyMB4XDTE5MDMyMjEzNTgyNloXDTI5MDMyMjEzNTgyNlowDjEMMAoG\nA1UEAxMDd2ViMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEADPv1RHVNRfa2VKR\nAB16b6rZnEt7tuhaxCFpQXPj7M2omb0B9Favq5E0ivpNtv1QnFhxtPd7d5k4e+T7\nSkW1TaOCAXIwggFuMA4GA1UdDwEB/wQEAwIDuDAdBgNVHSUEFjAUBggrBgEFBQcD\nAgYIKwYBBQUHAwEwDAYDVR0TAQH/BAIwADBoBgNVHQ4EYQRfN2Q6MDc6ODc6M2E6\nNDA6MTk6NDc6YzM6NWE6YzA6YmE6NjI6ZGY6YWY6NGI6ZDQ6MDU6MjU6NzY6M2Q6\nNWE6OGQ6MTY6OGQ6Njc6NWU6MmU6YTA6MzQ6N2Q6ZGM6ZmYwagYDVR0jBGMwYYBf\nZDE6MTE6MTE6YWM6MmE6YmE6OTc6YjI6M2Y6YWM6N2I6YmQ6ZGE6YmU6YjE6OGE6\nZmM6OWE6YmE6YjU6YmM6ODM6ZTc6NWU6NDE6NmY6ZjI6NzM6OTU6NTg6MGM6ZGIw\nWQYDVR0RBFIwUIZOc3BpZmZlOi8vMTExMTExMTEtMjIyMi0zMzMzLTQ0NDQtNTU1\nNTU1NTU1NTU1LmNvbnN1bC9ucy9kZWZhdWx0L2RjL2RjMS9zdmMvd2ViMAoGCCqG\nSM49BAMCA0gAMEUCIGC3TTvvjj76KMrguVyFf4tjOqaSCRie3nmHMRNNRav7AiEA\npY0heYeK9A6iOLrzqxSerkXXQyj5e9bE4VgUnxgPU6g=\n-----END CERTIFICATE-----\n"
},
"privateKey": {
"inlineString": "-----BEGIN EC PRIVATE KEY-----\nMHcCAQEEIMoTkpRggp3fqZzFKh82yS4LjtJI+XY+qX/7DefHFrtdoAoGCCqGSM49\nAwEHoUQDQgAEADPv1RHVNRfa2VKRAB16b6rZnEt7tuhaxCFpQXPj7M2omb0B9Fav\nq5E0ivpNtv1QnFhxtPd7d5k4e+T7SkW1TQ==\n-----END EC PRIVATE KEY-----\n"
}
}
],
"validationContext": {
"trustedCa": {
"inlineString": "-----BEGIN CERTIFICATE-----\nMIICXDCCAgKgAwIBAgIICpZq70Z9LyUwCgYIKoZIzj0EAwIwFDESMBAGA1UEAxMJ\nVGVzdCBDQSAyMB4XDTE5MDMyMjEzNTgyNloXDTI5MDMyMjEzNTgyNlowFDESMBAG\nA1UEAxMJVGVzdCBDQSAyMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEIhywH1gx\nAsMwuF3ukAI5YL2jFxH6Usnma1HFSfVyxbXX1/uoZEYrj8yCAtdU2yoHETyd+Zx2\nThhRLP79pYegCaOCATwwggE4MA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTAD\nAQH/MGgGA1UdDgRhBF9kMToxMToxMTphYzoyYTpiYTo5NzpiMjozZjphYzo3Yjpi\nZDpkYTpiZTpiMTo4YTpmYzo5YTpiYTpiNTpiYzo4MzplNzo1ZTo0MTo2ZjpmMjo3\nMzo5NTo1ODowYzpkYjBqBgNVHSMEYzBhgF9kMToxMToxMTphYzoyYTpiYTo5Nzpi\nMjozZjphYzo3YjpiZDpkYTpiZTpiMTo4YTpmYzo5YTpiYTpiNTpiYzo4MzplNzo1\nZTo0MTo2ZjpmMjo3Mzo5NTo1ODowYzpkYjA/BgNVHREEODA2hjRzcGlmZmU6Ly8x\nMTExMTExMS0yMjIyLTMzMzMtNDQ0NC01NTU1NTU1NTU1NTUuY29uc3VsMAoGCCqG\nSM49BAMCA0gAMEUCICOY0i246rQHJt8o8Oya0D5PLL1FnmsQmQqIGCi31RwnAiEA\noR5f6Ku+cig2Il8T8LJujOp2/2A72QcHZA57B13y+8o=\n-----END CERTIFICATE-----\n"
},
"matchSubjectAltNames": [
{
"exact": "spiffe://11111111-2222-3333-4444-555555555555.consul/ns/default/dc/dc1/svc/db"
}
]
}
},
"sni": "db.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul"
}
}
}
],
"typeUrl": "type.googleapis.com/envoy.config.cluster.v3.Cluster",
"nonce": "00000001"
}

View File

@ -0,0 +1,70 @@
{
"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",
"type": "EDS",
"edsClusterConfig": {
"edsConfig": {
"ads": {
},
"resourceApiVersion": "V3"
}
},
"connectTimeout": "33s",
"circuitBreakers": {
"thresholds": [
{
"maxConnections": 4096
}
]
},
"outlierDetection": {
"consecutive5xx": 10,
"interval": "5s"
},
"commonLbConfig": {
"healthyPanicThreshold": {
}
},
"transportSocket": {
"name": "tls",
"typedConfig": {
"@type": "type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext",
"commonTlsContext": {
"tlsParams": {
},
"tlsCertificates": [
{
"certificateChain": {
"inlineString": "-----BEGIN CERTIFICATE-----\nMIICjDCCAjKgAwIBAgIIC5llxGV1gB8wCgYIKoZIzj0EAwIwFDESMBAGA1UEAxMJ\nVGVzdCBDQSAyMB4XDTE5MDMyMjEzNTgyNloXDTI5MDMyMjEzNTgyNlowDjEMMAoG\nA1UEAxMDd2ViMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEADPv1RHVNRfa2VKR\nAB16b6rZnEt7tuhaxCFpQXPj7M2omb0B9Favq5E0ivpNtv1QnFhxtPd7d5k4e+T7\nSkW1TaOCAXIwggFuMA4GA1UdDwEB/wQEAwIDuDAdBgNVHSUEFjAUBggrBgEFBQcD\nAgYIKwYBBQUHAwEwDAYDVR0TAQH/BAIwADBoBgNVHQ4EYQRfN2Q6MDc6ODc6M2E6\nNDA6MTk6NDc6YzM6NWE6YzA6YmE6NjI6ZGY6YWY6NGI6ZDQ6MDU6MjU6NzY6M2Q6\nNWE6OGQ6MTY6OGQ6Njc6NWU6MmU6YTA6MzQ6N2Q6ZGM6ZmYwagYDVR0jBGMwYYBf\nZDE6MTE6MTE6YWM6MmE6YmE6OTc6YjI6M2Y6YWM6N2I6YmQ6ZGE6YmU6YjE6OGE6\nZmM6OWE6YmE6YjU6YmM6ODM6ZTc6NWU6NDE6NmY6ZjI6NzM6OTU6NTg6MGM6ZGIw\nWQYDVR0RBFIwUIZOc3BpZmZlOi8vMTExMTExMTEtMjIyMi0zMzMzLTQ0NDQtNTU1\nNTU1NTU1NTU1LmNvbnN1bC9ucy9kZWZhdWx0L2RjL2RjMS9zdmMvd2ViMAoGCCqG\nSM49BAMCA0gAMEUCIGC3TTvvjj76KMrguVyFf4tjOqaSCRie3nmHMRNNRav7AiEA\npY0heYeK9A6iOLrzqxSerkXXQyj5e9bE4VgUnxgPU6g=\n-----END CERTIFICATE-----\n"
},
"privateKey": {
"inlineString": "-----BEGIN EC PRIVATE KEY-----\nMHcCAQEEIMoTkpRggp3fqZzFKh82yS4LjtJI+XY+qX/7DefHFrtdoAoGCCqGSM49\nAwEHoUQDQgAEADPv1RHVNRfa2VKRAB16b6rZnEt7tuhaxCFpQXPj7M2omb0B9Fav\nq5E0ivpNtv1QnFhxtPd7d5k4e+T7SkW1TQ==\n-----END EC PRIVATE KEY-----\n"
}
}
],
"validationContext": {
"trustedCa": {
"inlineString": "-----BEGIN CERTIFICATE-----\nMIICXDCCAgKgAwIBAgIICpZq70Z9LyUwCgYIKoZIzj0EAwIwFDESMBAGA1UEAxMJ\nVGVzdCBDQSAyMB4XDTE5MDMyMjEzNTgyNloXDTI5MDMyMjEzNTgyNlowFDESMBAG\nA1UEAxMJVGVzdCBDQSAyMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEIhywH1gx\nAsMwuF3ukAI5YL2jFxH6Usnma1HFSfVyxbXX1/uoZEYrj8yCAtdU2yoHETyd+Zx2\nThhRLP79pYegCaOCATwwggE4MA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTAD\nAQH/MGgGA1UdDgRhBF9kMToxMToxMTphYzoyYTpiYTo5NzpiMjozZjphYzo3Yjpi\nZDpkYTpiZTpiMTo4YTpmYzo5YTpiYTpiNTpiYzo4MzplNzo1ZTo0MTo2ZjpmMjo3\nMzo5NTo1ODowYzpkYjBqBgNVHSMEYzBhgF9kMToxMToxMTphYzoyYTpiYTo5Nzpi\nMjozZjphYzo3YjpiZDpkYTpiZTpiMTo4YTpmYzo5YTpiYTpiNTpiYzo4MzplNzo1\nZTo0MTo2ZjpmMjo3Mzo5NTo1ODowYzpkYjA/BgNVHREEODA2hjRzcGlmZmU6Ly8x\nMTExMTExMS0yMjIyLTMzMzMtNDQ0NC01NTU1NTU1NTU1NTUuY29uc3VsMAoGCCqG\nSM49BAMCA0gAMEUCICOY0i246rQHJt8o8Oya0D5PLL1FnmsQmQqIGCi31RwnAiEA\noR5f6Ku+cig2Il8T8LJujOp2/2A72QcHZA57B13y+8o=\n-----END CERTIFICATE-----\n"
},
"matchSubjectAltNames": [
{
"exact": "spiffe://11111111-2222-3333-4444-555555555555.consul/ns/default/dc/dc1/svc/db"
}
]
}
},
"sni": "db.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul"
}
}
}
],
"typeUrl": "type.googleapis.com/envoy.config.cluster.v3.Cluster",
"nonce": "00000001"
}

View File

@ -17,6 +17,9 @@
} }
}, },
"connectTimeout": "33s", "connectTimeout": "33s",
"outlierDetection": {
},
"lbPolicy": "CLUSTER_PROVIDED" "lbPolicy": "CLUSTER_PROVIDED"
}, },
{ {

View File

@ -17,6 +17,9 @@
} }
}, },
"connectTimeout": "33s", "connectTimeout": "33s",
"outlierDetection": {
},
"lbPolicy": "CLUSTER_PROVIDED" "lbPolicy": "CLUSTER_PROVIDED"
}, },
{ {

View File

@ -17,6 +17,9 @@
} }
}, },
"connectTimeout": "33s", "connectTimeout": "33s",
"outlierDetection": {
},
"lbPolicy": "CLUSTER_PROVIDED" "lbPolicy": "CLUSTER_PROVIDED"
}, },
{ {

View File

@ -17,6 +17,9 @@
} }
}, },
"connectTimeout": "33s", "connectTimeout": "33s",
"outlierDetection": {
},
"lbPolicy": "CLUSTER_PROVIDED" "lbPolicy": "CLUSTER_PROVIDED"
}, },
{ {

View File

@ -16,6 +16,9 @@
} }
}, },
"connectTimeout": "33s", "connectTimeout": "33s",
"outlierDetection": {
},
"lbPolicy": "CLUSTER_PROVIDED" "lbPolicy": "CLUSTER_PROVIDED"
}, },
{ {

View File

@ -16,6 +16,9 @@
} }
}, },
"connectTimeout": "33s", "connectTimeout": "33s",
"outlierDetection": {
},
"lbPolicy": "CLUSTER_PROVIDED" "lbPolicy": "CLUSTER_PROVIDED"
}, },
{ {

View File

@ -16,6 +16,9 @@
} }
}, },
"connectTimeout": "33s", "connectTimeout": "33s",
"outlierDetection": {
},
"lbPolicy": "CLUSTER_PROVIDED" "lbPolicy": "CLUSTER_PROVIDED"
}, },
{ {

View File

@ -16,6 +16,9 @@
} }
}, },
"connectTimeout": "33s", "connectTimeout": "33s",
"outlierDetection": {
},
"lbPolicy": "CLUSTER_PROVIDED" "lbPolicy": "CLUSTER_PROVIDED"
}, },
{ {

View File

@ -44,6 +44,10 @@ type IngressServiceConfig struct {
MaxConnections *uint32 MaxConnections *uint32
MaxPendingRequests *uint32 MaxPendingRequests *uint32
MaxConcurrentRequests *uint32 MaxConcurrentRequests *uint32
// PassiveHealthCheck configuration determines how upstream proxy instances will
// be monitored for removal from the load balancing pool.
PassiveHealthCheck *PassiveHealthCheck `json:",omitempty" alias:"passive_health_check"`
} }
type GatewayTLSConfig struct { type GatewayTLSConfig struct {
@ -137,6 +141,10 @@ type IngressService struct {
MaxConnections *uint32 `json:",omitempty" alias:"max_connections"` MaxConnections *uint32 `json:",omitempty" alias:"max_connections"`
MaxPendingRequests *uint32 `json:",omitempty" alias:"max_pending_requests"` MaxPendingRequests *uint32 `json:",omitempty" alias:"max_pending_requests"`
MaxConcurrentRequests *uint32 `json:",omitempty" alias:"max_concurrent_requests"` MaxConcurrentRequests *uint32 `json:",omitempty" alias:"max_concurrent_requests"`
// PassiveHealthCheck configuration determines how upstream proxy instances will
// be monitored for removal from the load balancing pool.
PassiveHealthCheck *PassiveHealthCheck `json:",omitempty" alias:"passive_health_check"`
} }
func (i *IngressGatewayConfigEntry) GetKind() string { return i.Kind } func (i *IngressGatewayConfigEntry) GetKind() string { return i.Kind }

View File

@ -2,6 +2,7 @@ package api
import ( import (
"testing" "testing"
"time"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
@ -32,6 +33,10 @@ func TestAPI_ConfigEntries_IngressGateway(t *testing.T) {
Defaults: &IngressServiceConfig{ Defaults: &IngressServiceConfig{
MaxConnections: uint32Pointer(2048), MaxConnections: uint32Pointer(2048),
MaxPendingRequests: uint32Pointer(4096), MaxPendingRequests: uint32Pointer(4096),
PassiveHealthCheck: &PassiveHealthCheck{
MaxFailures: 20,
Interval: 500000000,
},
}, },
} }
@ -100,6 +105,9 @@ func TestAPI_ConfigEntries_IngressGateway(t *testing.T) {
MaxConnections: uint32Pointer(5120), MaxConnections: uint32Pointer(5120),
MaxPendingRequests: uint32Pointer(512), MaxPendingRequests: uint32Pointer(512),
MaxConcurrentRequests: uint32Pointer(2048), MaxConcurrentRequests: uint32Pointer(2048),
PassiveHealthCheck: &PassiveHealthCheck{
MaxFailures: 10,
},
}, },
}, },
TLS: &GatewayTLSConfig{ TLS: &GatewayTLSConfig{
@ -178,6 +186,10 @@ func TestAPI_ConfigEntries_IngressGateway(t *testing.T) {
require.Equal(t, *ingress2.Defaults.MaxConnections, *readIngress.Defaults.MaxConnections) require.Equal(t, *ingress2.Defaults.MaxConnections, *readIngress.Defaults.MaxConnections)
require.Equal(t, uint32(4096), *readIngress.Defaults.MaxPendingRequests) require.Equal(t, uint32(4096), *readIngress.Defaults.MaxPendingRequests)
require.Equal(t, uint32(0), *readIngress.Defaults.MaxConcurrentRequests) require.Equal(t, uint32(0), *readIngress.Defaults.MaxConcurrentRequests)
require.Equal(t, uint32(20), readIngress.Defaults.PassiveHealthCheck.MaxFailures)
require.Equal(t, time.Duration(500000000), readIngress.Defaults.PassiveHealthCheck.Interval)
require.Nil(t, readIngress.Defaults.PassiveHealthCheck.EnforcingConsecutive5xx)
require.Len(t, readIngress.Listeners, 1) require.Len(t, readIngress.Listeners, 1)
require.Len(t, readIngress.Listeners[0].Services, 1) require.Len(t, readIngress.Listeners[0].Services, 1)
// Set namespace and partition to blank so that OSS and ent can utilize the same tests // Set namespace and partition to blank so that OSS and ent can utilize the same tests

View File

@ -304,6 +304,11 @@ func IngressServiceToStructs(s *IngressService, t *structs.IngressService) {
t.MaxConnections = s.MaxConnections t.MaxConnections = s.MaxConnections
t.MaxPendingRequests = s.MaxPendingRequests t.MaxPendingRequests = s.MaxPendingRequests
t.MaxConcurrentRequests = s.MaxConcurrentRequests t.MaxConcurrentRequests = s.MaxConcurrentRequests
if s.PassiveHealthCheck != nil {
var x structs.PassiveHealthCheck
PassiveHealthCheckToStructs(s.PassiveHealthCheck, &x)
t.PassiveHealthCheck = &x
}
t.Meta = s.Meta t.Meta = s.Meta
t.EnterpriseMeta = enterpriseMetaToStructs(s.EnterpriseMeta) t.EnterpriseMeta = enterpriseMetaToStructs(s.EnterpriseMeta)
} }
@ -331,6 +336,11 @@ func IngressServiceFromStructs(t *structs.IngressService, s *IngressService) {
s.MaxConnections = t.MaxConnections s.MaxConnections = t.MaxConnections
s.MaxPendingRequests = t.MaxPendingRequests s.MaxPendingRequests = t.MaxPendingRequests
s.MaxConcurrentRequests = t.MaxConcurrentRequests s.MaxConcurrentRequests = t.MaxConcurrentRequests
if t.PassiveHealthCheck != nil {
var x PassiveHealthCheck
PassiveHealthCheckFromStructs(t.PassiveHealthCheck, &x)
s.PassiveHealthCheck = &x
}
s.Meta = t.Meta s.Meta = t.Meta
s.EnterpriseMeta = enterpriseMetaFromStructs(t.EnterpriseMeta) s.EnterpriseMeta = enterpriseMetaFromStructs(t.EnterpriseMeta)
} }
@ -341,6 +351,11 @@ func IngressServiceConfigToStructs(s *IngressServiceConfig, t *structs.IngressSe
t.MaxConnections = s.MaxConnections t.MaxConnections = s.MaxConnections
t.MaxPendingRequests = s.MaxPendingRequests t.MaxPendingRequests = s.MaxPendingRequests
t.MaxConcurrentRequests = s.MaxConcurrentRequests t.MaxConcurrentRequests = s.MaxConcurrentRequests
if s.PassiveHealthCheck != nil {
var x structs.PassiveHealthCheck
PassiveHealthCheckToStructs(s.PassiveHealthCheck, &x)
t.PassiveHealthCheck = &x
}
} }
func IngressServiceConfigFromStructs(t *structs.IngressServiceConfig, s *IngressServiceConfig) { func IngressServiceConfigFromStructs(t *structs.IngressServiceConfig, s *IngressServiceConfig) {
if s == nil { if s == nil {
@ -349,6 +364,11 @@ func IngressServiceConfigFromStructs(t *structs.IngressServiceConfig, s *Ingress
s.MaxConnections = t.MaxConnections s.MaxConnections = t.MaxConnections
s.MaxPendingRequests = t.MaxPendingRequests s.MaxPendingRequests = t.MaxPendingRequests
s.MaxConcurrentRequests = t.MaxConcurrentRequests s.MaxConcurrentRequests = t.MaxConcurrentRequests
if t.PassiveHealthCheck != nil {
var x PassiveHealthCheck
PassiveHealthCheckFromStructs(t.PassiveHealthCheck, &x)
s.PassiveHealthCheck = &x
}
} }
func IntentionHTTPHeaderPermissionToStructs(s *IntentionHTTPHeaderPermission, t *structs.IntentionHTTPHeaderPermission) { func IntentionHTTPHeaderPermissionToStructs(s *IntentionHTTPHeaderPermission, t *structs.IntentionHTTPHeaderPermission) {
if s == nil { if s == nil {

File diff suppressed because it is too large Load Diff

View File

@ -242,6 +242,7 @@ message IngressServiceConfig {
uint32 MaxConnections = 1; uint32 MaxConnections = 1;
uint32 MaxPendingRequests = 2; uint32 MaxPendingRequests = 2;
uint32 MaxConcurrentRequests = 3; uint32 MaxConcurrentRequests = 3;
PassiveHealthCheck PassiveHealthCheck = 4;
} }
// mog annotation: // mog annotation:
@ -300,6 +301,7 @@ message IngressService {
uint32 MaxConnections = 8; uint32 MaxConnections = 8;
uint32 MaxPendingRequests = 9; uint32 MaxPendingRequests = 9;
uint32 MaxConcurrentRequests = 10; uint32 MaxConcurrentRequests = 10;
PassiveHealthCheck PassiveHealthCheck = 11;
} }
// mog annotation: // mog annotation:

View File

@ -10,6 +10,9 @@ services {
local_bind_port = 5000 local_bind_port = 5000
config { config {
protocol = "http" protocol = "http"
passive_health_check {
interval = "22s"
}
} }
} }
] ]

View File

@ -69,6 +69,15 @@ load helpers
[ "$UPS" = "envoy.filters.http.router" ] [ "$UPS" = "envoy.filters.http.router" ]
} }
@test "s1 proxy should have been configured with passive_health_check" {
CLUSTER_CONFIG=$(get_envoy_cluster_config localhost:19000 1a47f6e1~s2.default.primary)
echo $CLUSTER_CONFIG
[ "$(echo $CLUSTER_CONFIG | jq --raw-output '.outlier_detection.interval')" = "22s" ]
[ "$(echo $CLUSTER_CONFIG | jq --raw-output '.outlier_detection.consecutive_5xx')" = null ]
[ "$(echo $CLUSTER_CONFIG | jq --raw-output '.outlier_detection.enforcing_consecutive_5xx')" = null ]
}
@test "s2 proxy should have been configured with http rbac filters" { @test "s2 proxy should have been configured with http rbac filters" {
HTTP_FILTERS=$(get_envoy_http_filters localhost:19001) HTTP_FILTERS=$(get_envoy_http_filters localhost:19001)
PUB=$(echo "$HTTP_FILTERS" | grep -E "^public_listener:" | cut -f 2 -d ' ') PUB=$(echo "$HTTP_FILTERS" | grep -E "^public_listener:" | cut -f 2 -d ' ')

View File

@ -23,11 +23,11 @@ load helpers
} }
@test "ingress-gateway should have healthy endpoints for s1" { @test "ingress-gateway should have healthy endpoints for s1" {
assert_upstream_has_endpoints_in_status 127.0.0.1:20000 s1 HEALTHY 1 assert_upstream_has_endpoints_in_status 127.0.0.1:20000 s1 HEALTHY 1
} }
@test "ingress-gateway should have healthy endpoints for s2" { @test "ingress-gateway should have healthy endpoints for s2" {
assert_upstream_has_endpoints_in_status 127.0.0.1:20000 s2 HEALTHY 1 assert_upstream_has_endpoints_in_status 127.0.0.1:20000 s2 HEALTHY 1
} }
@test "ingress should be able to connect to s1 via configured path" { @test "ingress should be able to connect to s1 via configured path" {

View File

@ -17,6 +17,11 @@ Defaults {
MaxConnections = 10 MaxConnections = 10
MaxPendingRequests = 20 MaxPendingRequests = 20
MaxConcurrentRequests = 30 MaxConcurrentRequests = 30
PassiveHealthCheck {
MaxFailures = 10
Interval = 5000000000
}
} }
listeners = [ listeners = [
{ {
@ -38,6 +43,9 @@ listeners = [
MaxConnections = 100 MaxConnections = 100
MaxPendingRequests = 200 MaxPendingRequests = 200
MaxConcurrentRequests = 300 MaxConcurrentRequests = 300
PassiveHealthCheck {
MaxFailures = 15
}
} }
] ]
} }

View File

@ -55,6 +55,23 @@ load helpers
[ "$MAX_REQS" = "30" ] [ "$MAX_REQS" = "30" ]
} }
@test "s2 proxy should have been configured with outlier detection in ingress gateway" {
CLUSTER_THRESHOLD=$(get_envoy_cluster_config 127.0.0.1:20000 s2.default.primary | jq '.outlier_detection')
echo $CLUSTER_THRESHOLD
INTERVAL=$(echo $CLUSTER_THRESHOLD | jq --raw-output '.interval')
CONSECTIVE5xx=$(echo $CLUSTER_THRESHOLD | jq --raw-output '.consecutive_5xx')
ENFORCING_CONSECTIVE5xx=$(echo $CLUSTER_THRESHOLD | jq --raw-output '.enforcing_consecutive_5xx')
echo "INTERVAL = $INTERVAL"
echo "CONSECTIVE5xx = $CONSECTIVE5xx"
echo "ENFORCING_CONSECTIVE5xx = $ENFORCING_CONSECTIVE5xx"
[ "$INTERVAL" = "5s" ]
[ "$CONSECTIVE5xx" = "10" ]
[ "$ENFORCING_CONSECTIVE5xx" = null ]
}
@test "ingress should be able to connect to s1 using Host header" { @test "ingress should be able to connect to s1 using Host header" {
assert_expected_fortio_name s1 s1.ingress.consul 9999 assert_expected_fortio_name s1 s1.ingress.consul 9999
} }

View File

@ -9,6 +9,9 @@ Defaults {
MaxConnections = 10 MaxConnections = 10
MaxPendingRequests = 20 MaxPendingRequests = 20
MaxConcurrentRequests = 30 MaxConcurrentRequests = 30
PassiveHealthCheck {
Interval = 5000000000
}
} }
listeners = [ listeners = [
{ {

View File

@ -39,6 +39,23 @@ load helpers
[ "$MAX_REQS" = "30" ] [ "$MAX_REQS" = "30" ]
} }
@test "s1 proxy should have been configured with outlier detection in ingress gateway" {
CLUSTER_THRESHOLD=$(get_envoy_cluster_config 127.0.0.1:20000 s1.default.primary | jq '.outlier_detection')
echo $CLUSTER_THRESHOLD
INTERVAL=$(echo $CLUSTER_THRESHOLD | jq --raw-output '.interval')
CONSECTIVE5xx=$(echo $CLUSTER_THRESHOLD | jq --raw-output '.consecutive_5xx')
ENFORCING_CONSECTIVE5xx=$(echo $CLUSTER_THRESHOLD | jq --raw-output '.enforcing_consecutive_5xx')
echo "INTERVAL = $INTERVAL"
echo "CONSECTIVE5xx = $CONSECTIVE5xx"
echo "ENFORCING_CONSECTIVE5xx = $ENFORCING_CONSECTIVE5xx"
[ "$INTERVAL" = "5s" ]
[ "$CONSECTIVE5xx" = null ]
[ "$ENFORCING_CONSECTIVE5xx" = null ]
}
@test "ingress should be able to connect to s1 via configured port" { @test "ingress should be able to connect to s1 via configured port" {
run retry_default curl -s -f -d hello localhost:9999 run retry_default curl -s -f -d hello localhost:9999
[ "$status" -eq 0 ] [ "$status" -eq 0 ]

View File

@ -30,9 +30,9 @@ load helpers
CLUSTER_THRESHOLD=$(get_envoy_cluster_config localhost:19000 s2.default.primary | jq '.circuit_breakers.thresholds[0]') CLUSTER_THRESHOLD=$(get_envoy_cluster_config localhost:19000 s2.default.primary | jq '.circuit_breakers.thresholds[0]')
echo $CLUSTER_THRESHOLD echo $CLUSTER_THRESHOLD
MAX_CONNS=$(echo $CLUSTER_THRESHOLD | jq --raw-output '.max_connections') MAX_CONNS=$(echo $CLUSTER_THRESHOLD | jq --raw-output '.max_connections')
MAX_PENDING_REQS=$(echo $CLUSTER_THRESHOLD | jq --raw-output '.max_pending_requests') MAX_PENDING_REQS=$(echo $CLUSTER_THRESHOLD | jq --raw-output '.max_pending_requests')
MAX_REQS=$(echo $CLUSTER_THRESHOLD | jq --raw-output '.max_requests') MAX_REQS=$(echo $CLUSTER_THRESHOLD | jq --raw-output '.max_requests')
echo "MAX_CONNS = $MAX_CONNS" echo "MAX_CONNS = $MAX_CONNS"
echo "MAX_PENDING_REQS = $MAX_PENDING_REQS" echo "MAX_PENDING_REQS = $MAX_PENDING_REQS"

View File

@ -1076,6 +1076,32 @@ You can specify the following parameters to configure ingress gateway configurat
respected, a L7 protocol must be defined in the \`protocol\` field. respected, a L7 protocol must be defined in the \`protocol\` field.
If not specified, it uses the default value. For example, 1024 for Envoy proxy.`, If not specified, it uses the default value. For example, 1024 for Envoy proxy.`,
}, },
{
name: 'PassiveHealthCheck',
type: 'PassiveHealthCheck: <optional>',
description:
'Passive health checks remove hosts from the upstream cluster that are unreachable or that return errors.',
children: [
{
name: 'interval',
type: 'int: <optional>',
description:
"The time in nanosecond between checks. Each check will cause hosts which have exceeded `max_failures` to be removed from the load balancer, and any hosts which have passed their ejection time to be returned to the load balancer. If not specified, it uses the default value. For example, 10s for Envoy proxy.",
},
{
name: 'max_failures',
type: 'int: <optional>',
description:
'The number of consecutive failures that cause a host to be removed from the upstream cluster. If not specified, Consul uses the proxy\'s default value. For example, `5` for Envoy proxy.',
},
{
name: 'enforcing_consecutive_5xx',
type: 'int: <optional>',
description:
'A percentage representing the chance that a host will be actually ejected when the proxy detects an outlier status through consecutive errors in the 500 code range. If not specified, Consul uses the proxy\'s default value. For example, `100` for Envoy proxy.',
},
],
},
], ],
}, },
{ {
@ -1208,6 +1234,25 @@ You can specify the following parameters to configure ingress gateway configurat
type: 'int: 0', type: 'int: 0',
description: 'overrides for the [`Defaults` field](#available-fields)', description: 'overrides for the [`Defaults` field](#available-fields)',
}, },
{
name: 'PassiveHealthCheck',
type: 'PassiveHealthCheck: <optional>',
description: 'overrides for the [`Defaults` field](#available-fields)',
children: [
{
name: 'interval',
type: 'int: <optional>',
},
{
name: 'max_failures',
type: 'int: <optional>',
},
{
name: 'enforcing_consecutive_5xx',
type: 'int: <optional>',
},
],
},
], ],
}, },
{ {

View File

@ -393,17 +393,21 @@ definition](/docs/connect/registration/service-registration) or
since HTTP/2 has many requests per connection. For this configuration to be since HTTP/2 has many requests per connection. For this configuration to be
respected, a L7 protocol must be defined in the `protocol` field. respected, a L7 protocol must be defined in the `protocol` field.
- `passive_health_check` - Passive health checks are used to remove hosts from - `passive_health_check` - Passive health checks remove hosts from the upstream
the upstream cluster which are unreachable or are returning errors. cluster that are unreachable or that return errors.
- `interval` - The time between checks. Each check will cause hosts which - `interval` - The time in nanosecond between checks. Each check will cause
have exceeded `max_failures` to be removed from the load balancer, and hosts which have exceeded `max_failures` to be removed from the load
any hosts which have passed their ejection time to be returned to the balancer, and any hosts which have passed their ejection time to be
load balancer. returned to the load balancer. If not specified, it uses the default value.
- `max_failures` - The number of consecutive failures which cause a host to be For example, 10s for Envoy proxy.
removed from the load balancer. - `max_failures` - The number of consecutive failures that cause a host to be
- `enforcing_consecutive_5xx` - The % chance that a host will be actually ejected removed from the upstream cluster. If not specified, Consul uses the proxy's
when an outlier status is detected through consecutive 5xx. default value. For example, `5` for Envoy proxy.
- `enforcing_consecutive_5xx` - A percentage representing the chance that a
host will be actually ejected when the proxy detects an outlier status
through consecutive errors in the 500 code range. If not specified, Consul
uses the proxy's default value. For example, `100` for Envoy proxy.
- `balance_outbound_connections` - Specifies the strategy for balancing outbound connections - `balance_outbound_connections` - Specifies the strategy for balancing outbound connections
across Envoy worker threads. Consul service mesh Envoy integration supports the across Envoy worker threads. Consul service mesh Envoy integration supports the