Skip to content

Commit ea3836f

Browse files
committed
Support for Listeners in object/mapping format
1 parent 959cefc commit ea3836f

File tree

5 files changed

+114
-29
lines changed

5 files changed

+114
-29
lines changed

docs/syntax/compose_x/elbv2.rst

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,55 @@ Similar syntax as for ECS Services Ingress, allow you to define Ingress. See the
135135
Listeners
136136
=========
137137

138+
.. hint::
139+
140+
Since version 1.1.8 you can define the listeners with a mapping, key being the port. For example
141+
142+
.. code-block::
143+
144+
x-elbv2:
145+
lbA:
146+
Properties:
147+
Type: application
148+
Scheme: internet-facing
149+
DnsAliases:
150+
- Route53Zone: x-route53::public-domain-01
151+
Names:
152+
- test.bdd-testing.compose-x.io
153+
- someother.test.bdd-testing.compose-x.io
154+
Settings:
155+
S3Logs: bucket:/prefix
156+
timeout_seconds: 60
157+
desync_mitigation_mode: defensive
158+
drop_invalid_header_fields: True
159+
http2: False
160+
cross_zone: True
161+
Listeners:
162+
80:
163+
Protocol: HTTP
164+
DefaultActions:
165+
- Redirect: HTTP_TO_HTTPS
166+
443:
167+
Protocol: HTTP
168+
Certificates:
169+
- x-acm: public-acm-01
170+
Targets:
171+
- name: bignicefamily:app01
172+
access: /somewhere
173+
8080:
174+
Protocol: HTTP
175+
Certificates:
176+
- x-acm: public-acm-01
177+
- CertificateArn: arn:aws:acm:eu-west-1:012345678912:certificate/102402a1-d0d2-46ff-b26b-33008f072ee8
178+
Targets:
179+
- name: bignicefamily:rproxy
180+
access: /
181+
- name: youtoo:rproxy
182+
access: /stupid
183+
- name: bignicefamily:app01
184+
access: thereisnospoon.ews-network.net:8080/abcd
185+
186+
138187
You can define in a very simple way your `Listener definition`_ and cross-reference other resources, here, the services
139188
and ACM certificates you might be creating.
140189

@@ -149,6 +198,10 @@ The following properties are identical to the original CFN definition.
149198

150199
`JSON Schema definition <https://github.com/compose-x/ecs_composex_specs/blob/main/ecs_composex_specs/x-elbv2.spec.json#L82>`__
151200

201+
.. note::
202+
203+
When using the dict/object format for Listener ports, the ``Port`` property defined is ignored as to avoid having conflicting values.
204+
152205
.. hint::
153206

154207
For certificates, you can also use **x-acm** to refer to an ACM certificate you are creating with this stack.

ecs_composex/elbv2/elbv2_stack/elbv2.py

Lines changed: 48 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import ecs_composex.common.troposphere_tools
99

1010
if TYPE_CHECKING:
11+
from troposphere import Template
1112
from ecs_composex.mods_manager import XResourceModule
1213
from ecs_composex.common.settings import ComposeXSettings
1314

@@ -133,34 +134,56 @@ def init_outputs(self):
133134
}
134135

135136
def set_listeners_from_list(self, listeners: list[dict], template) -> None:
137+
"""Transforms the list of listeners into a dict format."""
136138
ports = [listener["Port"] for listener in listeners]
137139
validate_listeners_duplicates(self.name, ports)
138-
for listener_def in listeners:
140+
dict_definition: dict[int, dict] = {}
141+
for _def in listeners:
142+
_port = int(_def["Port"])
143+
del _def["Port"]
144+
dict_definition[_port] = _def
145+
146+
self.set_listeners_from_dict(dict_definition, template)
147+
148+
def _define_targets(self, targets: list[dict]) -> None:
149+
"""
150+
Validates the targets definitions is correct
151+
152+
:raises: ValueError if the defined listener target does not comply to the LISTENER_TARGET_RE regex
153+
"""
154+
for target in targets:
155+
target_parts = LISTENER_TARGET_RE.match(target["name"])
156+
if not target_parts:
157+
raise ValueError(
158+
f"{self.module.res_key}.{self.name} - Listener {listener_def['Port']}"
159+
f" - Target {target['name']} is not a valid value. Must match",
160+
LISTENER_TARGET_RE.pattern,
161+
)
162+
simple_family: str = (
163+
f"{target_parts.group('family')}:{target_parts.group('container')}"
164+
)
165+
family_id: str = (
166+
simple_family + ":" + target_parts.group("port")
167+
if target_parts.group("port")
168+
else simple_family
169+
)
170+
if family_id not in self.services:
171+
listener_def["Targets"].remove(target)
172+
173+
def set_listeners_from_dict(self, listeners: dict, template: Template) -> None:
174+
"""
175+
Creates ComposeListener objects from the definition within x-elbv2.<LB>
176+
177+
"""
178+
for port, listener_def in listeners.items():
139179
targets: list[dict] = set_else_none("Targets", listener_def, [])
140180
if targets and self.services:
141-
for target in targets:
142-
target_parts = LISTENER_TARGET_RE.match(target["name"])
143-
if not target_parts:
144-
raise ValueError(
145-
f"{self.module.res_key}.{self.name} - Listener {listener_def['Port']}"
146-
f" - Target {target['name']} is not a valid value. Must match",
147-
LISTENER_TARGET_RE.pattern,
148-
)
149-
simple_family: str = (
150-
f"{target_parts.group('family')}:{target_parts.group('container')}"
151-
)
152-
family_id: str = (
153-
simple_family + ":" + target_parts.group("port")
154-
if target_parts.group("port")
155-
else simple_family
156-
)
157-
if family_id not in self.services:
158-
listener_def["Targets"].remove(target)
181+
self._define_targets(targets)
159182
if keyisset("Targets", listener_def) or keyisset(
160183
"DefaultActions", listener_def
161184
):
162185
new_listener = template.add_resource(
163-
ComposeListener(self, listener_def)
186+
ComposeListener(self, int(port), listener_def)
164187
)
165188
self.new_listeners.append(new_listener)
166189
else:
@@ -184,8 +207,12 @@ def set_listeners(self, template):
184207
)
185208
if isinstance(listeners, list):
186209
self.set_listeners_from_list(listeners, template)
210+
elif isinstance(listeners, dict):
211+
self.set_listeners_from_dict(listeners, template)
187212
else:
188-
raise TypeError("Listeners not yet supported as a mapping.")
213+
raise TypeError(
214+
"Listeners must be one of [list, dict]. Got", type(listeners)
215+
)
189216

190217
def find_lookup_listeners(self):
191218
"""

ecs_composex/elbv2/elbv2_stack/elbv2_listener/__init__.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -49,22 +49,24 @@ class ComposeListener(Listener):
4949

5050
targets_keys = "Targets"
5151

52-
def __init__(self, lb: Elbv2, definition):
52+
def __init__(self, lb: Elbv2, port: int, definition: dict):
5353
"""
5454
Method to init listener.
5555
5656
:param ecs_composex.elbv2.elbv2_stack.elbv2.Elbv2 lb:
5757
:param dict definition:
5858
"""
5959
self._lb = lb
60+
self._port: int = port
6061
self.definition = deepcopy(definition)
61-
straight_import_keys = ["Port", "Protocol", "SslPolicy", "AlpnPolicy"]
62+
straight_import_keys = ["Protocol", "SslPolicy", "AlpnPolicy"]
6263
listener_kwargs = {
6364
x: self.definition[x] for x in straight_import_keys if x in self.definition
6465
}
6566
listener_kwargs.update(
6667
{x: self.definition[x] for x in self.attributes if x in self.definition}
6768
)
69+
listener_kwargs.update({"Port": port})
6870
self.services = (
6971
self.definition[self.targets_keys]
7072
if keyisset(self.targets_keys, self.definition)
@@ -103,7 +105,7 @@ def __init__(self, lb: Elbv2, definition):
103105

104106
@property
105107
def def_port(self) -> int:
106-
return int(self.definition["Port"])
108+
return self._port
107109

108110
@property
109111
def lb(self) -> Elbv2:

ecs_composex/elbv2/x-elbv2.spec.json

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -92,8 +92,8 @@
9292
{
9393
"type": "object",
9494
"patternProperties": {
95-
"x-": {},
96-
"[0-9]{1,6}": {
95+
"^x-": {},
96+
"[0-9]{1,7}": {
9797
"$ref": "#/definitions/ListenerV2"
9898
}
9999
}
@@ -517,6 +517,9 @@
517517
},
518518
"ListenerV2": {
519519
"type": "object",
520+
"patternProperties": {
521+
"^x-": {}
522+
},
520523
"oneOf": [
521524
{
522525
"required": [

use-cases/elbv2/create_acm_parameters.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,18 +33,18 @@ x-elbv2:
3333
http2: False
3434
cross_zone: True
3535
Listeners:
36-
- Port: 80
36+
80:
3737
Protocol: HTTP
3838
DefaultActions:
3939
- Redirect: HTTP_TO_HTTPS
40-
- Port: 443
40+
443:
4141
Protocol: HTTP
4242
Certificates:
4343
- x-acm: public-acm-01
4444
Targets:
4545
- name: bignicefamily:app01
4646
access: /somewhere
47-
- Port: 8080
47+
8080:
4848
Protocol: HTTP
4949
Certificates:
5050
- x-acm: public-acm-01

0 commit comments

Comments
 (0)