Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions python/ambassador/envoy/v3/v3listener.py
Original file line number Diff line number Diff line change
Expand Up @@ -523,6 +523,15 @@ def base_http_config(self) -> Dict[str, Any]:
"idle_timeout": "%0.3fs" % (float(listener_idle_timeout_ms) / 1000.0)
}

listener_max_connection_lifetime_ms = self.config.ir.ambassador_module.get(
"listener_max_connection_lifetime_ms", None
)
if listener_max_connection_lifetime_ms:
common_http_options = base_http_config.setdefault("common_http_protocol_options", {})
common_http_options["max_connection_duration"] = "%0.3fs" % (
float(listener_max_connection_lifetime_ms) / 1000.0
)

if "headers_with_underscores_action" in self.config.ir.ambassador_module:
if "common_http_protocol_options" in base_http_config:
base_http_config["common_http_protocol_options"][
Expand Down
3 changes: 3 additions & 0 deletions python/ambassador/ir/ir.py
Original file line number Diff line number Diff line change
Expand Up @@ -1160,6 +1160,9 @@ def features(self) -> Dict[str, Any]:
od["listener_idle_timeout_ms"] = self.ambassador_module.get(
"listener_idle_timeout_ms", None
)
od["listener_max_connection_lifetime_ms"] = self.ambassador_module.get(
"listener_max_connection_lifetime_ms", None
)
od["headers_with_underscores_action"] = self.ambassador_module.get(
"headers_with_underscores_action", None
)
Expand Down
2 changes: 2 additions & 0 deletions python/ambassador/ir/irambassador.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ class IRAmbassador(IRResource):
"headers_with_underscores_action",
"keepalive",
"listener_idle_timeout_ms",
"listener_max_connection_lifetime_ms",
"liveness_probe",
"load_balancer",
"max_request_headers_kb",
Expand Down Expand Up @@ -132,6 +133,7 @@ def __init__(
envoy_validation_timeout=IRAmbassador.default_validation_timeout,
enable_ipv4=True,
listener_idle_timeout_ms=None,
listener_max_connection_lifetime_ms=None,
liveness_probe={"enabled": True},
readiness_probe={"enabled": True},
diagnostics={"enabled": True}, # TODO(lukeshu): In getambassador.io/v3alpha2, change
Expand Down
83 changes: 83 additions & 0 deletions python/tests/kat/t_listenermaxconnectionlifetime.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import json
from typing import Generator, Tuple, Union

from abstract_tests import HTTP, AmbassadorTest, Node, ServiceType
from kat.harness import Query


class ListenerMaxConnectionLifetime(AmbassadorTest):
target: ServiceType

def init(self):
self.target = HTTP()

def config(self) -> Generator[Union[str, Tuple[Node, str]], None, None]:
yield self, self.format(
"""
---
apiVersion: getambassador.io/v3alpha1
kind: Module
name: ambassador
ambassador_id: [{self.ambassador_id}]
config:
listener_max_connection_lifetime_ms: 3600000
"""
)
yield self, self.format(
"""
---
apiVersion: getambassador.io/v3alpha1
kind: Mapping
name: config__dump
hostname: "*"
prefix: /config_dump
rewrite: /config_dump
service: http://127.0.0.1:8001
"""
)

def queries(self):
yield Query(self.url("config_dump"), phase=2)

def check(self):
expected_val = "3600s"
actual_val = ""
assert self.results[0].body
body = json.loads(self.results[0].body)
for config_obj in body.get("configs"):
if config_obj.get("@type") == "type.googleapis.com/envoy.admin.v3.ListenersConfigDump":
listeners = config_obj.get("dynamic_listeners")
found_max_conn_duration = False
for listener_obj in listeners:
listener = listener_obj.get("active_state").get("listener")
filter_chains = listener.get("filter_chains")
for filters in filter_chains:
for filter in filters.get("filters"):
if (
filter.get("name")
== "envoy.filters.network.http_connection_manager"
):
filter_config = filter.get("typed_config")
common_http_protocol_options = filter_config.get(
"common_http_protocol_options"
)
if common_http_protocol_options:
actual_val = common_http_protocol_options.get(
"max_connection_duration", ""
)
if actual_val != "":
if actual_val == expected_val:
found_max_conn_duration = True
else:
assert (
False
), "Expected to find common_http_protocol_options.max_connection_duration property on listener"
else:
assert (
False
), "Expected to find common_http_protocol_options property on listener"
assert (
found_max_conn_duration
), "Expected common_http_protocol_options.max_connection_duration = {}, Got common_http_protocol_options.max_connection_duration = {}".format(
expected_val, actual_val
)
21 changes: 19 additions & 2 deletions python/tests/unit/test_listener_common_http_protocol_options.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,12 +40,29 @@ def test_listener_idle_timeout_ms():
_test_listener_common_http_protocol_options(yaml, expectations={"idle_timeout": "150.000s"})


@pytest.mark.compilertest
def test_listener_max_connection_lifetime_ms():
yaml = module_and_mapping_manifests(["listener_max_connection_lifetime_ms: 3600000"], [])
_test_listener_common_http_protocol_options(
yaml, expectations={"max_connection_duration": "3600.000s"}
)


@pytest.mark.compilertest
def test_all_listener_common_http_protocol_options():
yaml = module_and_mapping_manifests(
["headers_with_underscores_action: DROP_HEADER", "listener_idle_timeout_ms: 4005"], []
[
"headers_with_underscores_action: DROP_HEADER",
"listener_idle_timeout_ms: 4005",
"listener_max_connection_lifetime_ms: 3600005",
],
[],
)
_test_listener_common_http_protocol_options(
yaml,
expectations={"headers_with_underscores_action": "DROP_HEADER", "idle_timeout": "4.005s"},
expectations={
"headers_with_underscores_action": "DROP_HEADER",
"idle_timeout": "4.005s",
"max_connection_duration": "3600.005s",
},
)