mirror of https://github.com/status-im/consul.git
NET-5115 Add retry + timeout filters for api-gateway (#18324)
* squash, implement retry/timeout in consul core * update tests
This commit is contained in:
parent
bfc519f293
commit
e235c8be3c
|
@ -0,0 +1,3 @@
|
|||
```release-note:feature
|
||||
api-gateway: add retry and timeout filters
|
||||
```
|
|
@ -161,6 +161,28 @@ func httpRouteToDiscoveryChain(route structs.HTTPRouteConfigEntry) (*structs.Ser
|
|||
}
|
||||
}
|
||||
|
||||
if rule.Filters.RetryFilter != nil {
|
||||
if rule.Filters.RetryFilter.NumRetries != nil {
|
||||
destination.NumRetries = *rule.Filters.RetryFilter.NumRetries
|
||||
}
|
||||
if rule.Filters.RetryFilter.RetryOnConnectFailure != nil {
|
||||
destination.RetryOnConnectFailure = *rule.Filters.RetryFilter.RetryOnConnectFailure
|
||||
}
|
||||
|
||||
if len(rule.Filters.RetryFilter.RetryOn) > 0 {
|
||||
destination.RetryOn = rule.Filters.RetryFilter.RetryOn
|
||||
}
|
||||
|
||||
if len(rule.Filters.RetryFilter.RetryOnStatusCodes) > 0 {
|
||||
destination.RetryOnStatusCodes = rule.Filters.RetryFilter.RetryOnStatusCodes
|
||||
}
|
||||
}
|
||||
|
||||
if rule.Filters.TimeoutFilter != nil {
|
||||
destination.IdleTimeout = rule.Filters.TimeoutFilter.IdleTimeout
|
||||
destination.RequestTimeout = rule.Filters.TimeoutFilter.RequestTimeout
|
||||
}
|
||||
|
||||
// for each match rule a ServiceRoute is created for the service-router
|
||||
// if there are no rules a single route with the destination is set
|
||||
if len(rule.Matches) == 0 {
|
||||
|
@ -173,6 +195,7 @@ func httpRouteToDiscoveryChain(route structs.HTTPRouteConfigEntry) (*structs.Ser
|
|||
Destination: &destination,
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return router, splitters, defaults
|
||||
|
|
|
@ -7,6 +7,7 @@ import (
|
|||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/miekg/dns"
|
||||
|
||||
|
@ -417,8 +418,10 @@ type HTTPQueryMatch struct {
|
|||
// HTTPFilters specifies a list of filters used to modify a request
|
||||
// before it is routed to an upstream.
|
||||
type HTTPFilters struct {
|
||||
Headers []HTTPHeaderFilter
|
||||
URLRewrite *URLRewrite
|
||||
Headers []HTTPHeaderFilter
|
||||
URLRewrite *URLRewrite
|
||||
RetryFilter *RetryFilter
|
||||
TimeoutFilter *TimeoutFilter
|
||||
}
|
||||
|
||||
// HTTPHeaderFilter specifies how HTTP headers should be modified.
|
||||
|
@ -432,6 +435,18 @@ type URLRewrite struct {
|
|||
Path string
|
||||
}
|
||||
|
||||
type RetryFilter struct {
|
||||
NumRetries *uint32
|
||||
RetryOn []string
|
||||
RetryOnStatusCodes []uint32
|
||||
RetryOnConnectFailure *bool
|
||||
}
|
||||
|
||||
type TimeoutFilter struct {
|
||||
RequestTimeout time.Duration
|
||||
IdleTimeout time.Duration
|
||||
}
|
||||
|
||||
// HTTPRouteRule specifies the routing rules used to determine what upstream
|
||||
// service an HTTP request is routed to.
|
||||
type HTTPRouteRule struct {
|
||||
|
|
|
@ -383,6 +383,30 @@ func (o *HTTPRouteConfigEntry) DeepCopy() *HTTPRouteConfigEntry {
|
|||
cp.Rules[i2].Filters.URLRewrite = new(URLRewrite)
|
||||
*cp.Rules[i2].Filters.URLRewrite = *o.Rules[i2].Filters.URLRewrite
|
||||
}
|
||||
if o.Rules[i2].Filters.RetryFilter != nil {
|
||||
cp.Rules[i2].Filters.RetryFilter = new(RetryFilter)
|
||||
*cp.Rules[i2].Filters.RetryFilter = *o.Rules[i2].Filters.RetryFilter
|
||||
if o.Rules[i2].Filters.RetryFilter.NumRetries != nil {
|
||||
cp.Rules[i2].Filters.RetryFilter.NumRetries = new(uint32)
|
||||
*cp.Rules[i2].Filters.RetryFilter.NumRetries = *o.Rules[i2].Filters.RetryFilter.NumRetries
|
||||
}
|
||||
if o.Rules[i2].Filters.RetryFilter.RetryOn != nil {
|
||||
cp.Rules[i2].Filters.RetryFilter.RetryOn = make([]string, len(o.Rules[i2].Filters.RetryFilter.RetryOn))
|
||||
copy(cp.Rules[i2].Filters.RetryFilter.RetryOn, o.Rules[i2].Filters.RetryFilter.RetryOn)
|
||||
}
|
||||
if o.Rules[i2].Filters.RetryFilter.RetryOnStatusCodes != nil {
|
||||
cp.Rules[i2].Filters.RetryFilter.RetryOnStatusCodes = make([]uint32, len(o.Rules[i2].Filters.RetryFilter.RetryOnStatusCodes))
|
||||
copy(cp.Rules[i2].Filters.RetryFilter.RetryOnStatusCodes, o.Rules[i2].Filters.RetryFilter.RetryOnStatusCodes)
|
||||
}
|
||||
if o.Rules[i2].Filters.RetryFilter.RetryOnConnectFailure != nil {
|
||||
cp.Rules[i2].Filters.RetryFilter.RetryOnConnectFailure = new(bool)
|
||||
*cp.Rules[i2].Filters.RetryFilter.RetryOnConnectFailure = *o.Rules[i2].Filters.RetryFilter.RetryOnConnectFailure
|
||||
}
|
||||
}
|
||||
if o.Rules[i2].Filters.TimeoutFilter != nil {
|
||||
cp.Rules[i2].Filters.TimeoutFilter = new(TimeoutFilter)
|
||||
*cp.Rules[i2].Filters.TimeoutFilter = *o.Rules[i2].Filters.TimeoutFilter
|
||||
}
|
||||
if o.Rules[i2].Matches != nil {
|
||||
cp.Rules[i2].Matches = make([]HTTPMatch, len(o.Rules[i2].Matches))
|
||||
copy(cp.Rules[i2].Matches, o.Rules[i2].Matches)
|
||||
|
@ -427,6 +451,30 @@ func (o *HTTPRouteConfigEntry) DeepCopy() *HTTPRouteConfigEntry {
|
|||
cp.Rules[i2].Services[i4].Filters.URLRewrite = new(URLRewrite)
|
||||
*cp.Rules[i2].Services[i4].Filters.URLRewrite = *o.Rules[i2].Services[i4].Filters.URLRewrite
|
||||
}
|
||||
if o.Rules[i2].Services[i4].Filters.RetryFilter != nil {
|
||||
cp.Rules[i2].Services[i4].Filters.RetryFilter = new(RetryFilter)
|
||||
*cp.Rules[i2].Services[i4].Filters.RetryFilter = *o.Rules[i2].Services[i4].Filters.RetryFilter
|
||||
if o.Rules[i2].Services[i4].Filters.RetryFilter.NumRetries != nil {
|
||||
cp.Rules[i2].Services[i4].Filters.RetryFilter.NumRetries = new(uint32)
|
||||
*cp.Rules[i2].Services[i4].Filters.RetryFilter.NumRetries = *o.Rules[i2].Services[i4].Filters.RetryFilter.NumRetries
|
||||
}
|
||||
if o.Rules[i2].Services[i4].Filters.RetryFilter.RetryOn != nil {
|
||||
cp.Rules[i2].Services[i4].Filters.RetryFilter.RetryOn = make([]string, len(o.Rules[i2].Services[i4].Filters.RetryFilter.RetryOn))
|
||||
copy(cp.Rules[i2].Services[i4].Filters.RetryFilter.RetryOn, o.Rules[i2].Services[i4].Filters.RetryFilter.RetryOn)
|
||||
}
|
||||
if o.Rules[i2].Services[i4].Filters.RetryFilter.RetryOnStatusCodes != nil {
|
||||
cp.Rules[i2].Services[i4].Filters.RetryFilter.RetryOnStatusCodes = make([]uint32, len(o.Rules[i2].Services[i4].Filters.RetryFilter.RetryOnStatusCodes))
|
||||
copy(cp.Rules[i2].Services[i4].Filters.RetryFilter.RetryOnStatusCodes, o.Rules[i2].Services[i4].Filters.RetryFilter.RetryOnStatusCodes)
|
||||
}
|
||||
if o.Rules[i2].Services[i4].Filters.RetryFilter.RetryOnConnectFailure != nil {
|
||||
cp.Rules[i2].Services[i4].Filters.RetryFilter.RetryOnConnectFailure = new(bool)
|
||||
*cp.Rules[i2].Services[i4].Filters.RetryFilter.RetryOnConnectFailure = *o.Rules[i2].Services[i4].Filters.RetryFilter.RetryOnConnectFailure
|
||||
}
|
||||
}
|
||||
if o.Rules[i2].Services[i4].Filters.TimeoutFilter != nil {
|
||||
cp.Rules[i2].Services[i4].Filters.TimeoutFilter = new(TimeoutFilter)
|
||||
*cp.Rules[i2].Services[i4].Filters.TimeoutFilter = *o.Rules[i2].Services[i4].Filters.TimeoutFilter
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,12 +7,14 @@ import (
|
|||
"path/filepath"
|
||||
"sort"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
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"
|
||||
envoy_listener_v3 "github.com/envoyproxy/go-control-plane/envoy/config/listener/v3"
|
||||
envoy_route_v3 "github.com/envoyproxy/go-control-plane/envoy/config/route/v3"
|
||||
envoy_tls_v3 "github.com/envoyproxy/go-control-plane/envoy/extensions/transport_sockets/tls/v3"
|
||||
"k8s.io/utils/pointer"
|
||||
|
||||
"github.com/hashicorp/consul/agent/connect"
|
||||
"github.com/hashicorp/consul/agent/consul/discoverychain"
|
||||
|
@ -567,6 +569,90 @@ func getAPIGatewayGoldenTestCases(t *testing.T) []goldenTestCase {
|
|||
Remove: []string{"X-Header-Remove"},
|
||||
},
|
||||
},
|
||||
RetryFilter: &structs.RetryFilter{
|
||||
NumRetries: pointer.Uint32(3),
|
||||
RetryOn: []string{"cancelled"},
|
||||
RetryOnStatusCodes: []uint32{500},
|
||||
RetryOnConnectFailure: pointer.Bool(true),
|
||||
},
|
||||
TimeoutFilter: &structs.TimeoutFilter{
|
||||
IdleTimeout: time.Second * 30,
|
||||
RequestTimeout: time.Second * 30,
|
||||
},
|
||||
},
|
||||
Services: []structs.HTTPService{{
|
||||
Name: "service",
|
||||
}},
|
||||
}},
|
||||
Parents: []structs.ResourceReference{
|
||||
{
|
||||
Kind: structs.APIGateway,
|
||||
Name: "api-gateway",
|
||||
},
|
||||
},
|
||||
},
|
||||
}, []structs.InlineCertificateConfigEntry{{
|
||||
Kind: structs.InlineCertificate,
|
||||
Name: "certificate",
|
||||
PrivateKey: gatewayTestPrivateKey,
|
||||
Certificate: gatewayTestCertificate,
|
||||
}}, []proxycfg.UpdateEvent{{
|
||||
CorrelationID: "discovery-chain:" + serviceUID.String(),
|
||||
Result: &structs.DiscoveryChainResponse{
|
||||
Chain: serviceChain,
|
||||
},
|
||||
}, {
|
||||
CorrelationID: "upstream-target:" + serviceChain.ID() + ":" + serviceUID.String(),
|
||||
Result: &structs.IndexedCheckServiceNodes{
|
||||
Nodes: proxycfg.TestUpstreamNodes(t, "service"),
|
||||
},
|
||||
}})
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "api-gateway-with-http-route-timeoutfilter-one-set",
|
||||
create: func(t testinf.T) *proxycfg.ConfigSnapshot {
|
||||
return proxycfg.TestConfigSnapshotAPIGateway(t, "default", nil, func(entry *structs.APIGatewayConfigEntry, bound *structs.BoundAPIGatewayConfigEntry) {
|
||||
entry.Listeners = []structs.APIGatewayListener{
|
||||
{
|
||||
Name: "listener",
|
||||
Protocol: structs.ListenerProtocolHTTP,
|
||||
Port: 8080,
|
||||
},
|
||||
}
|
||||
bound.Listeners = []structs.BoundAPIGatewayListener{
|
||||
{
|
||||
Name: "listener",
|
||||
Certificates: []structs.ResourceReference{{
|
||||
Kind: structs.InlineCertificate,
|
||||
Name: "certificate",
|
||||
}},
|
||||
Routes: []structs.ResourceReference{{
|
||||
Kind: structs.HTTPRoute,
|
||||
Name: "route",
|
||||
}},
|
||||
},
|
||||
}
|
||||
}, []structs.BoundRoute{
|
||||
&structs.HTTPRouteConfigEntry{
|
||||
Kind: structs.HTTPRoute,
|
||||
Name: "route",
|
||||
Rules: []structs.HTTPRouteRule{{
|
||||
Filters: structs.HTTPFilters{
|
||||
Headers: []structs.HTTPHeaderFilter{
|
||||
{
|
||||
Add: map[string]string{
|
||||
"X-Header-Add": "added",
|
||||
},
|
||||
Set: map[string]string{
|
||||
"X-Header-Set": "set",
|
||||
},
|
||||
Remove: []string{"X-Header-Remove"},
|
||||
},
|
||||
},
|
||||
TimeoutFilter: &structs.TimeoutFilter{
|
||||
IdleTimeout: time.Second * 30,
|
||||
},
|
||||
},
|
||||
Services: []structs.HTTPService{{
|
||||
Name: "service",
|
||||
|
|
55
agent/xds/testdata/clusters/api-gateway-with-http-route-timeoutfilter-one-set.latest.golden
vendored
Normal file
55
agent/xds/testdata/clusters/api-gateway-with-http-route-timeoutfilter-one-set.latest.golden
vendored
Normal file
|
@ -0,0 +1,55 @@
|
|||
{
|
||||
"versionInfo": "00000001",
|
||||
"resources": [
|
||||
{
|
||||
"@type": "type.googleapis.com/envoy.config.cluster.v3.Cluster",
|
||||
"name": "service.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",
|
||||
"altStatName": "service.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",
|
||||
"type": "EDS",
|
||||
"edsClusterConfig": {
|
||||
"edsConfig": {
|
||||
"ads": {},
|
||||
"resourceApiVersion": "V3"
|
||||
}
|
||||
},
|
||||
"connectTimeout": "5s",
|
||||
"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/service"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"sni": "service.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"typeUrl": "type.googleapis.com/envoy.config.cluster.v3.Cluster",
|
||||
"nonce": "00000001"
|
||||
}
|
41
agent/xds/testdata/endpoints/api-gateway-with-http-route-timeoutfilter-one-set.latest.golden
vendored
Normal file
41
agent/xds/testdata/endpoints/api-gateway-with-http-route-timeoutfilter-one-set.latest.golden
vendored
Normal file
|
@ -0,0 +1,41 @@
|
|||
{
|
||||
"versionInfo": "00000001",
|
||||
"resources": [
|
||||
{
|
||||
"@type": "type.googleapis.com/envoy.config.endpoint.v3.ClusterLoadAssignment",
|
||||
"clusterName": "service.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",
|
||||
"endpoints": [
|
||||
{
|
||||
"lbEndpoints": [
|
||||
{
|
||||
"endpoint": {
|
||||
"address": {
|
||||
"socketAddress": {
|
||||
"address": "10.10.1.1",
|
||||
"portValue": 8080
|
||||
}
|
||||
}
|
||||
},
|
||||
"healthStatus": "HEALTHY",
|
||||
"loadBalancingWeight": 1
|
||||
},
|
||||
{
|
||||
"endpoint": {
|
||||
"address": {
|
||||
"socketAddress": {
|
||||
"address": "10.10.1.2",
|
||||
"portValue": 8080
|
||||
}
|
||||
}
|
||||
},
|
||||
"healthStatus": "HEALTHY",
|
||||
"loadBalancingWeight": 1
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"typeUrl": "type.googleapis.com/envoy.config.endpoint.v3.ClusterLoadAssignment",
|
||||
"nonce": "00000001"
|
||||
}
|
85
agent/xds/testdata/listeners/api-gateway-with-http-route-timeoutfilter-one-set.latest.golden
vendored
Normal file
85
agent/xds/testdata/listeners/api-gateway-with-http-route-timeoutfilter-one-set.latest.golden
vendored
Normal file
|
@ -0,0 +1,85 @@
|
|||
{
|
||||
"versionInfo": "00000001",
|
||||
"resources": [
|
||||
{
|
||||
"@type": "type.googleapis.com/envoy.config.listener.v3.Listener",
|
||||
"name": "http:1.2.3.4:8080",
|
||||
"address": {
|
||||
"socketAddress": {
|
||||
"address": "1.2.3.4",
|
||||
"portValue": 8080
|
||||
}
|
||||
},
|
||||
"filterChains": [
|
||||
{
|
||||
"filters": [
|
||||
{
|
||||
"name": "envoy.filters.network.http_connection_manager",
|
||||
"typedConfig": {
|
||||
"@type": "type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager",
|
||||
"statPrefix": "ingress_upstream_certificate",
|
||||
"rds": {
|
||||
"configSource": {
|
||||
"ads": {},
|
||||
"resourceApiVersion": "V3"
|
||||
},
|
||||
"routeConfigName": "8080"
|
||||
},
|
||||
"httpFilters": [
|
||||
{
|
||||
"name": "envoy.filters.http.router",
|
||||
"typedConfig": {
|
||||
"@type": "type.googleapis.com/envoy.extensions.filters.http.router.v3.Router"
|
||||
}
|
||||
}
|
||||
],
|
||||
"tracing": {
|
||||
"randomSampling": {}
|
||||
},
|
||||
"upgradeConfigs": [
|
||||
{
|
||||
"upgradeType": "websocket"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"transportSocket": {
|
||||
"name": "tls",
|
||||
"typedConfig": {
|
||||
"@type": "type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.DownstreamTlsContext",
|
||||
"commonTlsContext": {
|
||||
"tlsParams": {},
|
||||
"tlsCertificates": [
|
||||
{
|
||||
"certificateChain": {
|
||||
"inlineString": "-----BEGIN CERTIFICATE-----\nMIICljCCAX4CCQCQMDsYO8FrPjANBgkqhkiG9w0BAQsFADANMQswCQYDVQQGEwJV\nUzAeFw0yMjEyMjAxNzUwMjVaFw0yNzEyMTkxNzUwMjVaMA0xCzAJBgNVBAYTAlVT\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAx95Opa6t4lGEpiTUogEB\nptqOdam2ch4BHQGhNhX/MrDwwuZQhttBwMfngQ/wd9NmYEPAwj0dumUoAITIq6i2\njQlhqTodElkbsd5vWY8R/bxJWQSoNvVE12TlzECxGpJEiHt4W0r8pGffk+rvplji\nUyCfnT1kGF3znOSjK1hRMTn6RKWCyYaBvXQiB4SGilfLgJcEpOJKtISIxmZ+S409\ng9X5VU88/Bmmrz4cMyxce86Kc2ug5/MOv0CjWDJwlrv8njneV2zvraQ61DDwQftr\nXOvuCbO5IBRHMOBHiHTZ4rtGuhMaIr21V4vb6n8c4YzXiFvhUYcyX7rltGZzVd+W\nmQIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQBfCqoUIdPf/HGSbOorPyZWbyizNtHJ\nGL7x9cAeIYxpI5Y/WcO1o5v94lvrgm3FNfJoGKbV66+JxOge731FrfMpHplhar1Z\nRahYIzNLRBTLrwadLAZkApUpZvB8qDK4knsTWFYujNsylCww2A6ajzIMFNU4GkUK\nNtyHRuD+KYRmjXtyX1yHNqfGN3vOQmwavHq2R8wHYuBSc6LAHHV9vG+j0VsgMELO\nqwxn8SmLkSKbf2+MsQVzLCXXN5u+D8Yv+4py+oKP4EQ5aFZuDEx+r/G/31rTthww\nAAJAMaoXmoYVdgXV+CPuBb2M4XCpuzLu3bcA2PXm5ipSyIgntMKwXV7r\n-----END CERTIFICATE-----\n"
|
||||
},
|
||||
"privateKey": {
|
||||
"inlineString": "-----BEGIN RSA PRIVATE KEY-----\nMIIEowIBAAKCAQEAx95Opa6t4lGEpiTUogEBptqOdam2ch4BHQGhNhX/MrDwwuZQ\nhttBwMfngQ/wd9NmYEPAwj0dumUoAITIq6i2jQlhqTodElkbsd5vWY8R/bxJWQSo\nNvVE12TlzECxGpJEiHt4W0r8pGffk+rvpljiUyCfnT1kGF3znOSjK1hRMTn6RKWC\nyYaBvXQiB4SGilfLgJcEpOJKtISIxmZ+S409g9X5VU88/Bmmrz4cMyxce86Kc2ug\n5/MOv0CjWDJwlrv8njneV2zvraQ61DDwQftrXOvuCbO5IBRHMOBHiHTZ4rtGuhMa\nIr21V4vb6n8c4YzXiFvhUYcyX7rltGZzVd+WmQIDAQABAoIBACYvceUzp2MK4gYA\nGWPOP2uKbBdM0l+hHeNV0WAM+dHMfmMuL4pkT36ucqt0ySOLjw6rQyOZG5nmA6t9\nsv0g4ae2eCMlyDIeNi1Yavu4Wt6YX4cTXbQKThm83C6W2X9THKbauBbxD621bsDK\n7PhiGPN60yPue7YwFQAPqqD4YaK+s22HFIzk9gwM/rkvAUNwRv7SyHMiFe4Igc1C\nEev7iHWzvj5Heoz6XfF+XNF9DU+TieSUAdjd56VyUb8XL4+uBTOhHwLiXvAmfaMR\nHvpcxeKnYZusS6NaOxcUHiJnsLNWrxmJj9WEGgQzuLxcLjTe4vVmELVZD8t3QUKj\nPAxu8tUCgYEA7KIWVn9dfVpokReorFym+J8FzLwSktP9RZYEMonJo00i8aii3K9s\nu/aSwRWQSCzmON1ZcxZzWhwQF9usz6kGCk//9+4hlVW90GtNK0RD+j7sp4aT2JI8\n9eLEjTG+xSXa7XWe98QncjjL9lu/yrRncSTxHs13q/XP198nn2aYuQ8CgYEA2Dnt\nsRBzv0fFEvzzFv7G/5f85mouN38TUYvxNRTjBLCXl9DeKjDkOVZ2b6qlfQnYXIru\nH+W+v+AZEb6fySXc8FRab7lkgTMrwE+aeI4rkW7asVwtclv01QJ5wMnyT84AgDD/\nDgt/RThFaHgtU9TW5GOZveL+l9fVPn7vKFdTJdcCgYEArJ99zjHxwJ1whNAOk1av\n09UmRPm6TvRo4heTDk8oEoIWCNatoHI0z1YMLuENNSnT9Q280FFDayvnrY/qnD7A\nkktT/sjwJOG8q8trKzIMqQS4XWm2dxoPcIyyOBJfCbEY6XuRsUuePxwh5qF942EB\nyS9a2s6nC4Ix0lgPrqAIr48CgYBgS/Q6riwOXSU8nqCYdiEkBYlhCJrKpnJxF9T1\nofa0yPzKZP/8ZEfP7VzTwHjxJehQ1qLUW9pG08P2biH1UEKEWdzo8vT6wVJT1F/k\nHtTycR8+a+Hlk2SHVRHqNUYQGpuIe8mrdJ1as4Pd0d/F/P0zO9Rlh+mAsGPM8HUM\nT0+9gwKBgHDoerX7NTskg0H0t8O+iSMevdxpEWp34ZYa9gHiftTQGyrRgERCa7Gj\nnZPAxKb2JoWyfnu3v7G5gZ8fhDFsiOxLbZv6UZJBbUIh1MjJISpXrForDrC2QNLX\nkHrHfwBFDB3KMudhQknsJzEJKCL/KmFH6o0MvsoaT9yzEl3K+ah/\n-----END RSA PRIVATE KEY-----\n"
|
||||
}
|
||||
}
|
||||
],
|
||||
"alpnProtocols": [
|
||||
"http/1.1"
|
||||
]
|
||||
},
|
||||
"requireClientCertificate": false
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"listenerFilters": [
|
||||
{
|
||||
"name": "envoy.filters.listener.tls_inspector",
|
||||
"typedConfig": {
|
||||
"@type": "type.googleapis.com/envoy.extensions.filters.listener.tls_inspector.v3.TlsInspector"
|
||||
}
|
||||
}
|
||||
],
|
||||
"trafficDirection": "OUTBOUND"
|
||||
}
|
||||
],
|
||||
"typeUrl": "type.googleapis.com/envoy.config.listener.v3.Listener",
|
||||
"nonce": "00000001"
|
||||
}
|
51
agent/xds/testdata/routes/api-gateway-with-http-route-timeoutfilter-one-set.latest.golden
vendored
Normal file
51
agent/xds/testdata/routes/api-gateway-with-http-route-timeoutfilter-one-set.latest.golden
vendored
Normal file
|
@ -0,0 +1,51 @@
|
|||
{
|
||||
"versionInfo": "00000001",
|
||||
"resources": [
|
||||
{
|
||||
"@type": "type.googleapis.com/envoy.config.route.v3.RouteConfiguration",
|
||||
"name": "8080",
|
||||
"virtualHosts": [
|
||||
{
|
||||
"name": "api-gateway-listener-9b9265b",
|
||||
"domains": [
|
||||
"*",
|
||||
"*:8080"
|
||||
],
|
||||
"routes": [
|
||||
{
|
||||
"match": {
|
||||
"prefix": "/"
|
||||
},
|
||||
"route": {
|
||||
"cluster": "service.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",
|
||||
"idleTimeout": "30s"
|
||||
},
|
||||
"requestHeadersToAdd": [
|
||||
{
|
||||
"header": {
|
||||
"key": "X-Header-Add",
|
||||
"value": "added"
|
||||
},
|
||||
"append": true
|
||||
},
|
||||
{
|
||||
"header": {
|
||||
"key": "X-Header-Set",
|
||||
"value": "set"
|
||||
},
|
||||
"append": false
|
||||
}
|
||||
],
|
||||
"requestHeadersToRemove": [
|
||||
"X-Header-Remove"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"validateClusters": true
|
||||
}
|
||||
],
|
||||
"typeUrl": "type.googleapis.com/envoy.config.route.v3.RouteConfiguration",
|
||||
"nonce": "00000001"
|
||||
}
|
|
@ -1,50 +1,59 @@
|
|||
{
|
||||
"versionInfo": "00000001",
|
||||
"resources": [
|
||||
"versionInfo": "00000001",
|
||||
"resources": [
|
||||
{
|
||||
"@type": "type.googleapis.com/envoy.config.route.v3.RouteConfiguration",
|
||||
"name": "8080",
|
||||
"virtualHosts": [
|
||||
"@type": "type.googleapis.com/envoy.config.route.v3.RouteConfiguration",
|
||||
"name": "8080",
|
||||
"virtualHosts": [
|
||||
{
|
||||
"name": "api-gateway-listener-9b9265b",
|
||||
"domains": [
|
||||
"name": "api-gateway-listener-9b9265b",
|
||||
"domains": [
|
||||
"*",
|
||||
"*:8080"
|
||||
],
|
||||
"routes": [
|
||||
"routes": [
|
||||
{
|
||||
"match": {
|
||||
"prefix": "/"
|
||||
"match": {
|
||||
"prefix": "/"
|
||||
},
|
||||
"route": {
|
||||
"cluster": "service.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul"
|
||||
"route": {
|
||||
"cluster": "service.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",
|
||||
"timeout": "30s",
|
||||
"idleTimeout": "30s",
|
||||
"retryPolicy": {
|
||||
"retryOn": "cancelled,connect-failure,retriable-status-codes",
|
||||
"numRetries": 3,
|
||||
"retriableStatusCodes": [
|
||||
500
|
||||
]
|
||||
}
|
||||
},
|
||||
"requestHeadersToAdd": [
|
||||
"requestHeadersToAdd": [
|
||||
{
|
||||
"header": {
|
||||
"key": "X-Header-Add",
|
||||
"value": "added"
|
||||
"header": {
|
||||
"key": "X-Header-Add",
|
||||
"value": "added"
|
||||
},
|
||||
"append": true
|
||||
"append": true
|
||||
},
|
||||
{
|
||||
"header": {
|
||||
"key": "X-Header-Set",
|
||||
"value": "set"
|
||||
"header": {
|
||||
"key": "X-Header-Set",
|
||||
"value": "set"
|
||||
},
|
||||
"append": false
|
||||
"append": false
|
||||
}
|
||||
],
|
||||
"requestHeadersToRemove": [
|
||||
"requestHeadersToRemove": [
|
||||
"X-Header-Remove"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"validateClusters": true
|
||||
"validateClusters": true
|
||||
}
|
||||
],
|
||||
"typeUrl": "type.googleapis.com/envoy.config.route.v3.RouteConfiguration",
|
||||
"nonce": "00000001"
|
||||
"typeUrl": "type.googleapis.com/envoy.config.route.v3.RouteConfiguration",
|
||||
"nonce": "00000001"
|
||||
}
|
5
agent/xds/testdata/secrets/api-gateway-with-http-route-timeoutfilter-one-set.latest.golden
vendored
Normal file
5
agent/xds/testdata/secrets/api-gateway-with-http-route-timeoutfilter-one-set.latest.golden
vendored
Normal file
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"versionInfo": "00000001",
|
||||
"typeUrl": "type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.Secret",
|
||||
"nonce": "00000001"
|
||||
}
|
|
@ -3,6 +3,8 @@
|
|||
|
||||
package api
|
||||
|
||||
import "time"
|
||||
|
||||
// TCPRouteConfigEntry -- TODO stub
|
||||
type TCPRouteConfigEntry struct {
|
||||
// Kind of the config entry. This should be set to api.TCPRoute.
|
||||
|
@ -195,8 +197,10 @@ type HTTPQueryMatch struct {
|
|||
// HTTPFilters specifies a list of filters used to modify a request
|
||||
// before it is routed to an upstream.
|
||||
type HTTPFilters struct {
|
||||
Headers []HTTPHeaderFilter
|
||||
URLRewrite *URLRewrite
|
||||
Headers []HTTPHeaderFilter
|
||||
URLRewrite *URLRewrite
|
||||
RetryFilter *RetryFilter
|
||||
TimeoutFilter *TimeoutFilter
|
||||
}
|
||||
|
||||
// HTTPHeaderFilter specifies how HTTP headers should be modified.
|
||||
|
@ -210,6 +214,18 @@ type URLRewrite struct {
|
|||
Path string
|
||||
}
|
||||
|
||||
type RetryFilter struct {
|
||||
NumRetries *uint32
|
||||
RetryOn []string
|
||||
RetryOnStatusCodes []uint32
|
||||
RetryOnConnectFailure *bool
|
||||
}
|
||||
|
||||
type TimeoutFilter struct {
|
||||
RequestTimeout time.Duration
|
||||
IdleTimeout time.Duration
|
||||
}
|
||||
|
||||
// HTTPRouteRule specifies the routing rules used to determine what upstream
|
||||
// service an HTTP request is routed to.
|
||||
type HTTPRouteRule struct {
|
||||
|
|
2
go.mod
2
go.mod
|
@ -118,6 +118,7 @@ require (
|
|||
k8s.io/api v0.26.2
|
||||
k8s.io/apimachinery v0.26.2
|
||||
k8s.io/client-go v0.26.2
|
||||
k8s.io/utils v0.0.0-20230220204549-a5ecb0141aa5
|
||||
)
|
||||
|
||||
require (
|
||||
|
@ -257,7 +258,6 @@ require (
|
|||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
k8s.io/klog/v2 v2.90.1 // indirect
|
||||
k8s.io/kube-openapi v0.0.0-20221012153701-172d655c2280 // indirect
|
||||
k8s.io/utils v0.0.0-20230220204549-a5ecb0141aa5 // indirect
|
||||
sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2 // indirect
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect
|
||||
sigs.k8s.io/yaml v1.3.0 // indirect
|
||||
|
|
|
@ -369,6 +369,16 @@ func HTTPFiltersToStructs(s *HTTPFilters, t *structs.HTTPFilters) {
|
|||
URLRewriteToStructs(s.URLRewrite, &x)
|
||||
t.URLRewrite = &x
|
||||
}
|
||||
if s.RetryFilter != nil {
|
||||
var x structs.RetryFilter
|
||||
RetryFilterToStructs(s.RetryFilter, &x)
|
||||
t.RetryFilter = &x
|
||||
}
|
||||
if s.TimeoutFilter != nil {
|
||||
var x structs.TimeoutFilter
|
||||
TimeoutFilterToStructs(s.TimeoutFilter, &x)
|
||||
t.TimeoutFilter = &x
|
||||
}
|
||||
}
|
||||
func HTTPFiltersFromStructs(t *structs.HTTPFilters, s *HTTPFilters) {
|
||||
if s == nil {
|
||||
|
@ -389,6 +399,16 @@ func HTTPFiltersFromStructs(t *structs.HTTPFilters, s *HTTPFilters) {
|
|||
URLRewriteFromStructs(t.URLRewrite, &x)
|
||||
s.URLRewrite = &x
|
||||
}
|
||||
if t.RetryFilter != nil {
|
||||
var x RetryFilter
|
||||
RetryFilterFromStructs(t.RetryFilter, &x)
|
||||
s.RetryFilter = &x
|
||||
}
|
||||
if t.TimeoutFilter != nil {
|
||||
var x TimeoutFilter
|
||||
TimeoutFilterFromStructs(t.TimeoutFilter, &x)
|
||||
s.TimeoutFilter = &x
|
||||
}
|
||||
}
|
||||
func HTTPHeaderFilterToStructs(s *HTTPHeaderFilter, t *structs.HTTPHeaderFilter) {
|
||||
if s == nil {
|
||||
|
@ -1650,6 +1670,24 @@ func ResourceReferenceFromStructs(t *structs.ResourceReference, s *ResourceRefer
|
|||
s.SectionName = t.SectionName
|
||||
s.EnterpriseMeta = enterpriseMetaFromStructs(t.EnterpriseMeta)
|
||||
}
|
||||
func RetryFilterToStructs(s *RetryFilter, t *structs.RetryFilter) {
|
||||
if s == nil {
|
||||
return
|
||||
}
|
||||
t.NumRetries = &s.NumRetries
|
||||
t.RetryOn = s.RetryOn
|
||||
t.RetryOnStatusCodes = s.RetryOnStatusCodes
|
||||
t.RetryOnConnectFailure = &s.RetryOnConnectFailure
|
||||
}
|
||||
func RetryFilterFromStructs(t *structs.RetryFilter, s *RetryFilter) {
|
||||
if s == nil {
|
||||
return
|
||||
}
|
||||
s.NumRetries = *t.NumRetries
|
||||
s.RetryOn = t.RetryOn
|
||||
s.RetryOnStatusCodes = t.RetryOnStatusCodes
|
||||
s.RetryOnConnectFailure = *t.RetryOnConnectFailure
|
||||
}
|
||||
func RetryPolicyBackOffToStructs(s *RetryPolicyBackOff, t *structs.RetryPolicyBackOff) {
|
||||
if s == nil {
|
||||
return
|
||||
|
@ -2224,6 +2262,20 @@ func TCPServiceFromStructs(t *structs.TCPService, s *TCPService) {
|
|||
s.Name = t.Name
|
||||
s.EnterpriseMeta = enterpriseMetaFromStructs(t.EnterpriseMeta)
|
||||
}
|
||||
func TimeoutFilterToStructs(s *TimeoutFilter, t *structs.TimeoutFilter) {
|
||||
if s == nil {
|
||||
return
|
||||
}
|
||||
t.RequestTimeout = structs.DurationFromProto(s.RequestTimeout)
|
||||
t.IdleTimeout = structs.DurationFromProto(s.IdleTimeout)
|
||||
}
|
||||
func TimeoutFilterFromStructs(t *structs.TimeoutFilter, s *TimeoutFilter) {
|
||||
if s == nil {
|
||||
return
|
||||
}
|
||||
s.RequestTimeout = structs.DurationToProto(t.RequestTimeout)
|
||||
s.IdleTimeout = structs.DurationToProto(t.IdleTimeout)
|
||||
}
|
||||
func TransparentProxyConfigToStructs(s *TransparentProxyConfig, t *structs.TransparentProxyConfig) {
|
||||
if s == nil {
|
||||
return
|
||||
|
|
|
@ -627,6 +627,26 @@ func (msg *URLRewrite) UnmarshalBinary(b []byte) error {
|
|||
return proto.Unmarshal(b, msg)
|
||||
}
|
||||
|
||||
// MarshalBinary implements encoding.BinaryMarshaler
|
||||
func (msg *RetryFilter) MarshalBinary() ([]byte, error) {
|
||||
return proto.Marshal(msg)
|
||||
}
|
||||
|
||||
// UnmarshalBinary implements encoding.BinaryUnmarshaler
|
||||
func (msg *RetryFilter) UnmarshalBinary(b []byte) error {
|
||||
return proto.Unmarshal(b, msg)
|
||||
}
|
||||
|
||||
// MarshalBinary implements encoding.BinaryMarshaler
|
||||
func (msg *TimeoutFilter) MarshalBinary() ([]byte, error) {
|
||||
return proto.Marshal(msg)
|
||||
}
|
||||
|
||||
// UnmarshalBinary implements encoding.BinaryUnmarshaler
|
||||
func (msg *TimeoutFilter) UnmarshalBinary(b []byte) error {
|
||||
return proto.Unmarshal(b, msg)
|
||||
}
|
||||
|
||||
// MarshalBinary implements encoding.BinaryMarshaler
|
||||
func (msg *HTTPHeaderFilter) MarshalBinary() ([]byte, error) {
|
||||
return proto.Marshal(msg)
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -884,6 +884,8 @@ message HTTPQueryMatch {
|
|||
message HTTPFilters {
|
||||
repeated HTTPHeaderFilter Headers = 1;
|
||||
URLRewrite URLRewrite = 2;
|
||||
RetryFilter RetryFilter = 3;
|
||||
TimeoutFilter TimeoutFilter = 4;
|
||||
}
|
||||
|
||||
// mog annotation:
|
||||
|
@ -895,6 +897,30 @@ message URLRewrite {
|
|||
string Path = 1;
|
||||
}
|
||||
|
||||
// mog annotation:
|
||||
//
|
||||
// target=github.com/hashicorp/consul/agent/structs.RetryFilter
|
||||
// output=config_entry.gen.go
|
||||
// name=Structs
|
||||
message RetryFilter {
|
||||
uint32 NumRetries = 1;
|
||||
repeated string RetryOn = 2;
|
||||
repeated uint32 RetryOnStatusCodes = 3;
|
||||
bool RetryOnConnectFailure = 4;
|
||||
}
|
||||
|
||||
// mog annotation:
|
||||
//
|
||||
// target=github.com/hashicorp/consul/agent/structs.TimeoutFilter
|
||||
// output=config_entry.gen.go
|
||||
// name=Structs
|
||||
message TimeoutFilter {
|
||||
// mog: func-to=structs.DurationFromProto func-from=structs.DurationToProto
|
||||
google.protobuf.Duration RequestTimeout = 1;
|
||||
// mog: func-to=structs.DurationFromProto func-from=structs.DurationToProto
|
||||
google.protobuf.Duration IdleTimeout = 2;
|
||||
}
|
||||
|
||||
// mog annotation:
|
||||
//
|
||||
// target=github.com/hashicorp/consul/agent/structs.HTTPHeaderFilter
|
||||
|
|
Loading…
Reference in New Issue