Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for configuring Envoys route idle_timeout #14340

Merged
merged 26 commits into from Nov 29, 2022
Merged
Show file tree
Hide file tree
Changes from 25 commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
794df09
Add idleTimeout
oulman Aug 23, 2022
5b35a2e
add idle_timeout
oulman Aug 23, 2022
b6c6c0e
fix tests
oulman Aug 25, 2022
ed05801
Merge branch 'hashicorp:main' into oulman-add-idle-timeout
oulman Aug 25, 2022
b218c99
add docs for local_request_timeout_ms
oulman Aug 25, 2022
137373c
Merge branch 'oulman-add-idle-timeout' of github.com:oulman/consul in…
oulman Aug 25, 2022
8592e23
add changelog entry
oulman Aug 25, 2022
af6a012
correct IdleTimeout struct field comment
oulman Aug 25, 2022
7fb0fb9
fix formatting
oulman Aug 25, 2022
9e4d33b
remove EOF newlines
oulman Aug 25, 2022
ff4a3ea
Update website/content/docs/connect/proxies/envoy.mdx
oulman Aug 25, 2022
e5cc5ea
Merge branch 'main' into oulman-add-idle-timeout
oulman Sep 2, 2022
077685d
Merge branch 'main' into oulman-add-idle-timeout
oulman Nov 2, 2022
c63dd09
go fmt
oulman Nov 2, 2022
1e48f24
Merge branch 'main' into oulman-add-idle-timeout
oulman Nov 2, 2022
b0676ab
Merge branch 'main' into oulman-add-idle-timeout
oulman Nov 15, 2022
6d6b804
Merge branch 'main' into oulman-add-idle-timeout
oulman Nov 16, 2022
19a2fb2
Merge branch 'main' into oulman-add-idle-timeout
oulman Nov 17, 2022
20836ac
remove trailing newline.
oulman Nov 28, 2022
7d40e16
Update local_idle_timeout_ms based on PR suggestion.
oulman Nov 28, 2022
881fc86
Merge branch 'main' into oulman-add-idle-timeout
oulman Nov 28, 2022
4a644c0
Merge branch 'main' into oulman-add-idle-timeout
oulman Nov 28, 2022
e078ad2
add missing test for idle_timeout xds generation
oulman Nov 29, 2022
e4f2124
Merge branch 'main' into oulman-add-idle-timeout
oulman Nov 29, 2022
e8948b4
Merge branch 'main' into oulman-add-idle-timeout
oulman Nov 29, 2022
8579c6f
Merge branch 'main' into oulman-add-idle-timeout
oulman Nov 29, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 4 additions & 0 deletions .changelog/14340.txt
@@ -0,0 +1,4 @@
```release-note:feature
connect: Add local_idle_timeout_ms to allow configuring the Envoy route idle timeout on local_app
connect: Add IdleTimeout to service-router to allow configuring the Envoy route idle timeout
```
3 changes: 3 additions & 0 deletions agent/config/runtime_test.go
Expand Up @@ -4107,6 +4107,7 @@ func TestLoad_IntegrationWithFlags(t *testing.T) {
"namespace" : "leek",
"prefix_rewrite" : "/alternate",
"request_timeout" : "99s",
"idle_timeout" : "99s",
"num_retries" : 12345,
"retry_on_connect_failure": true,
"retry_on_status_codes" : [401, 209]
Expand Down Expand Up @@ -4195,6 +4196,7 @@ func TestLoad_IntegrationWithFlags(t *testing.T) {
namespace = "leek"
prefix_rewrite = "/alternate"
request_timeout = "99s"
idle_timeout = "99s"
num_retries = 12345
retry_on_connect_failure = true
retry_on_status_codes = [401, 209]
Expand Down Expand Up @@ -4284,6 +4286,7 @@ func TestLoad_IntegrationWithFlags(t *testing.T) {
Partition: acl.DefaultPartitionName,
PrefixRewrite: "/alternate",
RequestTimeout: 99 * time.Second,
IdleTimeout: 99 * time.Second,
NumRetries: 12345,
RetryOnConnectFailure: true,
RetryOnStatusCodes: []uint32{401, 209},
Expand Down
9 changes: 9 additions & 0 deletions agent/proxycfg/testing_upstreams.go
Expand Up @@ -682,6 +682,15 @@ func setupTestVariationDiscoveryChain(
RequestTimeout: 33 * time.Second,
},
},
{
Match: httpMatch(&structs.ServiceRouteHTTPMatch{
PathPrefix: "/idle-timeout",
}),
Destination: &structs.ServiceRouteDestination{
Service: "idle-timeout",
IdleTimeout: 33 * time.Second,
},
},
{
Match: httpMatch(&structs.ServiceRouteHTTPMatch{
PathPrefix: "/retry-connect",
Expand Down
16 changes: 16 additions & 0 deletions agent/structs/config_entry_discoverychain.go
Expand Up @@ -426,6 +426,10 @@ type ServiceRouteDestination struct {
// downstream request (and retries) to be processed.
RequestTimeout time.Duration `json:",omitempty" alias:"request_timeout"`

// IdleTimeout is The total amount of time permitted for the request stream
// to be idle
IdleTimeout time.Duration `json:",omitempty" alias:"idle_timeout"`

// NumRetries is the number of times to retry the request when a retryable
// result occurs. This seems fairly proxy agnostic.
NumRetries uint32 `json:",omitempty" alias:"num_retries"`
Expand All @@ -452,22 +456,29 @@ func (e *ServiceRouteDestination) MarshalJSON() ([]byte, error) {
type Alias ServiceRouteDestination
exported := &struct {
RequestTimeout string `json:",omitempty"`
IdleTimeout string `json:",omitempty"`
*Alias
}{
RequestTimeout: e.RequestTimeout.String(),
IdleTimeout: e.IdleTimeout.String(),
Alias: (*Alias)(e),
}
if e.RequestTimeout == 0 {
exported.RequestTimeout = ""
}

if e.IdleTimeout == 0 {
exported.IdleTimeout = ""
}

return json.Marshal(exported)
}

func (e *ServiceRouteDestination) UnmarshalJSON(data []byte) error {
type Alias ServiceRouteDestination
aux := &struct {
RequestTimeout string
IdleTimeout string
*Alias
}{
Alias: (*Alias)(e),
Expand All @@ -481,6 +492,11 @@ func (e *ServiceRouteDestination) UnmarshalJSON(data []byte) error {
return err
}
}
if aux.IdleTimeout != "" {
if e.IdleTimeout, err = time.ParseDuration(aux.IdleTimeout); err != nil {
return err
}
}
return nil
}

Expand Down
3 changes: 3 additions & 0 deletions agent/structs/config_entry_test.go
Expand Up @@ -603,6 +603,7 @@ func TestDecodeConfigEntry(t *testing.T) {
namespace = "leek"
prefix_rewrite = "/alternate"
request_timeout = "99s"
idle_timeout = "99s"
num_retries = 12345
retry_on_connect_failure = true
retry_on_status_codes = [401, 209]
Expand Down Expand Up @@ -704,6 +705,7 @@ func TestDecodeConfigEntry(t *testing.T) {
Namespace = "leek"
PrefixRewrite = "/alternate"
RequestTimeout = "99s"
IdleTimeout = "99s"
NumRetries = 12345
RetryOnConnectFailure = true
RetryOnStatusCodes = [401, 209]
Expand Down Expand Up @@ -805,6 +807,7 @@ func TestDecodeConfigEntry(t *testing.T) {
Namespace: "leek",
PrefixRewrite: "/alternate",
RequestTimeout: 99 * time.Second,
IdleTimeout: 99 * time.Second,
NumRetries: 12345,
RetryOnConnectFailure: true,
RetryOnStatusCodes: []uint32{401, 209},
Expand Down
5 changes: 5 additions & 0 deletions agent/xds/config.go
Expand Up @@ -49,6 +49,11 @@ type ProxyConfig struct {
// respected (15s)
LocalRequestTimeoutMs *int `mapstructure:"local_request_timeout_ms"`

// LocalIdleTimeoutMs is the number of milliseconds to timeout HTTP streams
// to the local app instance. If not set, no value is set, Envoy defaults are
// respected (300s)
LocalIdleTimeoutMs *int `mapstructure:"local_idle_timeout_ms"`

// Protocol describes the service's protocol. Valid values are "tcp",
// "http" and "grpc". Anything else is treated as tcp. This enables
// protocol aware features like per-request metrics and connection
Expand Down
33 changes: 33 additions & 0 deletions agent/xds/config_test.go
Expand Up @@ -157,6 +157,39 @@ func TestParseProxyConfig(t *testing.T) {
Protocol: "tcp",
},
},
{
name: "local idle timeout override, float ",
input: map[string]interface{}{
"local_idle_timeout_ms": float64(1000.0),
},
want: ProxyConfig{
LocalConnectTimeoutMs: 5000,
LocalIdleTimeoutMs: intPointer(1000),
Protocol: "tcp",
},
},
{
name: "local idle timeout override, int ",
input: map[string]interface{}{
"local_idle_timeout_ms": 1000,
},
want: ProxyConfig{
LocalConnectTimeoutMs: 5000,
LocalIdleTimeoutMs: intPointer(1000),
Protocol: "tcp",
},
},
{
name: "local idle timeout override, string",
input: map[string]interface{}{
"local_idle_timeout_ms": "1000",
},
want: ProxyConfig{
LocalConnectTimeoutMs: 5000,
LocalIdleTimeoutMs: intPointer(1000),
Protocol: "tcp",
},
},
{
name: "balance inbound connections override, string",
input: map[string]interface{}{
Expand Down
7 changes: 7 additions & 0 deletions agent/xds/listeners.go
Expand Up @@ -1275,6 +1275,7 @@ func (s *ResourceGenerator) makeInboundListener(cfgSnap *proxycfg.ConfigSnapshot
routeName: name,
cluster: LocalAppClusterName,
requestTimeoutMs: cfg.LocalRequestTimeoutMs,
idleTimeoutMs: cfg.LocalIdleTimeoutMs,
tracing: tracing,
}
if useHTTPFilter {
Expand Down Expand Up @@ -2114,6 +2115,7 @@ type listenerFilterOpts struct {
statPrefix string
routePath string
requestTimeoutMs *int
idleTimeoutMs *int
ingressGateway bool
httpAuthzFilter *envoy_http_v3.HttpFilter
forwardClientDetails bool
Expand Down Expand Up @@ -2260,6 +2262,11 @@ func makeHTTPFilter(opts listenerFilterOpts) (*envoy_listener_v3.Filter, error)
r.Timeout = durationpb.New(time.Duration(*opts.requestTimeoutMs) * time.Millisecond)
}

if opts.idleTimeoutMs != nil {
r := route.GetRoute()
r.IdleTimeout = durationpb.New(time.Duration(*opts.idleTimeoutMs) * time.Millisecond)
}

// If a path is provided, do not match on a catch-all prefix
if opts.routePath != "" {
route.Match.PathSpecifier = &envoy_route_v3.RouteMatch_Path{Path: opts.routePath}
Expand Down
1 change: 1 addition & 0 deletions agent/xds/listeners_test.go
Expand Up @@ -230,6 +230,7 @@ func TestListenersFromSnapshot(t *testing.T) {
ns.Proxy.Config["protocol"] = "http"
ns.Proxy.Config["local_connect_timeout_ms"] = 1234
ns.Proxy.Config["local_request_timeout_ms"] = 2345
ns.Proxy.Config["local_idle_timeout_ms"] = 3456
}, nil)
},
},
Expand Down
4 changes: 4 additions & 0 deletions agent/xds/routes.go
Expand Up @@ -577,6 +577,10 @@ func (s *ResourceGenerator) makeUpstreamRouteForDiscoveryChain(
routeAction.Route.Timeout = durationpb.New(destination.RequestTimeout)
}

if destination.IdleTimeout > 0 {
routeAction.Route.IdleTimeout = durationpb.New(destination.IdleTimeout)
}

if destination.HasRetryFeatures() {
routeAction.Route.RetryPolicy = getRetryPolicyForDestination(destination)
}
Expand Down
Expand Up @@ -83,7 +83,8 @@
},
"route": {
"cluster": "local_app",
"timeout": "2.345s"
"timeout": "2.345s",
"idleTimeout": "3.456s"
}
}
]
Expand Down
Expand Up @@ -274,6 +274,15 @@
"timeout": "33s"
}
},
{
"match": {
"prefix": "/idle-timeout"
},
"route": {
"cluster": "idle-timeout.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",
"idleTimeout": "33s"
}
},
{
"match": {
"prefix": "/retry-connect"
Expand Down
Expand Up @@ -275,6 +275,15 @@
"timeout": "33s"
}
},
{
"match": {
"prefix": "/idle-timeout"
},
"route": {
"cluster": "idle-timeout.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",
"idleTimeout": "33s"
}
},
{
"match": {
"prefix": "/retry-connect"
Expand Down
Expand Up @@ -275,6 +275,15 @@
"timeout": "33s"
}
},
{
"match": {
"prefix": "/idle-timeout"
},
"route": {
"cluster": "idle-timeout.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",
"idleTimeout": "33s"
}
},
{
"match": {
"prefix": "/retry-connect"
Expand Down
12 changes: 12 additions & 0 deletions api/config_entry_discoverychain.go
Expand Up @@ -69,6 +69,7 @@ type ServiceRouteDestination struct {
Partition string `json:",omitempty"`
PrefixRewrite string `json:",omitempty" alias:"prefix_rewrite"`
RequestTimeout time.Duration `json:",omitempty" alias:"request_timeout"`
IdleTimeout time.Duration `json:",omitempty" alias:"idle_timeout"`
NumRetries uint32 `json:",omitempty" alias:"num_retries"`
RetryOnConnectFailure bool `json:",omitempty" alias:"retry_on_connect_failure"`
RetryOnStatusCodes []uint32 `json:",omitempty" alias:"retry_on_status_codes"`
Expand All @@ -81,14 +82,19 @@ func (e *ServiceRouteDestination) MarshalJSON() ([]byte, error) {
type Alias ServiceRouteDestination
exported := &struct {
RequestTimeout string `json:",omitempty"`
IdleTimeout string `json:",omitempty"`
*Alias
}{
RequestTimeout: e.RequestTimeout.String(),
IdleTimeout: e.IdleTimeout.String(),
Alias: (*Alias)(e),
}
if e.RequestTimeout == 0 {
exported.RequestTimeout = ""
}
if e.IdleTimeout == 0 {
exported.IdleTimeout = ""
}

return json.Marshal(exported)
}
Expand All @@ -97,6 +103,7 @@ func (e *ServiceRouteDestination) UnmarshalJSON(data []byte) error {
type Alias ServiceRouteDestination
aux := &struct {
RequestTimeout string
IdleTimeout string
*Alias
}{
Alias: (*Alias)(e),
Expand All @@ -110,6 +117,11 @@ func (e *ServiceRouteDestination) UnmarshalJSON(data []byte) error {
return err
}
}
if aux.IdleTimeout != "" {
if e.IdleTimeout, err = time.ParseDuration(aux.IdleTimeout); err != nil {
return err
}
}
return nil
}

Expand Down
2 changes: 2 additions & 0 deletions api/config_entry_test.go
Expand Up @@ -612,6 +612,7 @@ func TestDecodeConfigEntry(t *testing.T) {
"Namespace": "leek",
"PrefixRewrite": "/alternate",
"RequestTimeout": "99s",
"IdleTimeout": "99s",
"NumRetries": 12345,
"RetryOnConnectFailure": true,
"RetryOnStatusCodes": [401, 209]
Expand Down Expand Up @@ -696,6 +697,7 @@ func TestDecodeConfigEntry(t *testing.T) {
Namespace: "leek",
PrefixRewrite: "/alternate",
RequestTimeout: 99 * time.Second,
IdleTimeout: 99 * time.Second,
NumRetries: 12345,
RetryOnConnectFailure: true,
RetryOnStatusCodes: []uint32{401, 209},
Expand Down
5 changes: 5 additions & 0 deletions command/helpers/helpers_test.go
Expand Up @@ -821,6 +821,7 @@ func TestParseConfigEntry(t *testing.T) {
partition = "chard"
prefix_rewrite = "/alternate"
request_timeout = "99s"
idle_timeout = "99s"
num_retries = 12345
retry_on_connect_failure = true
retry_on_status_codes = [401, 209]
Expand Down Expand Up @@ -906,6 +907,7 @@ func TestParseConfigEntry(t *testing.T) {
Partition = "chard"
PrefixRewrite = "/alternate"
RequestTimeout = "99s"
IdleTimeout = "99s"
NumRetries = 12345
RetryOnConnectFailure = true
RetryOnStatusCodes = [401, 209]
Expand Down Expand Up @@ -992,6 +994,7 @@ func TestParseConfigEntry(t *testing.T) {
"partition": "chard",
"prefix_rewrite": "/alternate",
"request_timeout": "99s",
"idle_timeout": "99s",
"num_retries": 12345,
"retry_on_connect_failure": true,
"retry_on_status_codes": [
Expand Down Expand Up @@ -1085,6 +1088,7 @@ func TestParseConfigEntry(t *testing.T) {
"Partition": "chard",
"PrefixRewrite": "/alternate",
"RequestTimeout": "99s",
"IdleTimeout": "99s",
"NumRetries": 12345,
"RetryOnConnectFailure": true,
"RetryOnStatusCodes": [
Expand Down Expand Up @@ -1177,6 +1181,7 @@ func TestParseConfigEntry(t *testing.T) {
Partition: "chard",
PrefixRewrite: "/alternate",
RequestTimeout: 99 * time.Second,
IdleTimeout: 99 * time.Second,
NumRetries: 12345,
RetryOnConnectFailure: true,
RetryOnStatusCodes: []uint32{401, 209},
Expand Down
Expand Up @@ -696,6 +696,12 @@ spec:
description:
'The total amount of time permitted for the entire downstream request (and retries) to be processed.',
},
{
name: 'IdleTimeout',
type: 'duration: 0',
description:
'The total amount of time permitted for the request stream to be idle',
},
{
name: 'NumRetries',
type: 'int: 0',
Expand Down