Skip to content

feat: add port-range support for haproxy-route-tcp#532

Draft
swetha1654 wants to merge 1 commit into
canonical:mainfrom
swetha1654:feat/port-range-tcp
Draft

feat: add port-range support for haproxy-route-tcp#532
swetha1654 wants to merge 1 commit into
canonical:mainfrom
swetha1654:feat/port-range-tcp

Conversation

@swetha1654
Copy link
Copy Markdown
Contributor

Summary

Implements port-range support for the haproxy-route-tcp interface as requested in issue #525.

This allows haproxy-route-tcp requirers to specify a port range (e.g. "10500-10600") instead of a single port. HAProxy will bind to the entire range and forward each incoming connection to the backend on the same port (1:1 passthrough via set-dst-port fc_dst_port).

Changes

Library (lib/charms/haproxy/v1/haproxy_route_tcp.py, LIBPATCH 3→4)

  • Added port_range: Optional[str] field to TcpRequirerApplicationData
  • Added validators:
    • validate_port_or_port_range: exactly one of port or port_range must be set; format ^\d+-\d+$ with start < end
    • assign_default_backend_port: raises if backend_port is set with port_range
    • sni_set_when_not_enforcing_tls: raises if sni is set with port_range
    • tls_terminate_requires_no_port_range: raises if tls_terminate=True with port_range (passthrough mode only)
  • Updated check_ports_unique to expand ranges for overlap detection
  • Updated provide_haproxy_route_tcp_requirements signature to accept port_range
  • Fixed update_relation_data guard to also accept port_range (was checking only port)

State (src/state/haproxy_route_tcp.py, src/state/haproxy_route.py)

  • HaproxyRouteTcpServer.port is now Optional[int] (None for port-range passthrough)
  • HAProxyRouteTcpBackend.servers: sets port=None, check=None for port_range backends
  • HAProxyRouteTcpFrontend: new port_range field, frontend_name, bind_address, all_frontend_ports properties
  • parse_haproxy_route_tcp_requirers_data: groups by port_range string as key
  • check_tcp_http_port_conflicts / valid_tcp_frontends: expand ranges for conflict detection

Template (templates/haproxy_route_tcp.cfg.j2)

  • Port-range frontend uses bind [::]:START-END v4v6
  • Adds tcp-request session set-dst-port fc_dst_port for dynamic port passthrough
  • Backend server rendered as server NAME IP:0 (port 0 = use set-dst-port value)
  • Health checks automatically disabled for port-range backends (port 0 incompatible)

Charm (src/charm.py)

  • set_ports and endpoint publishing use all_frontend_ports to handle both single ports and ranges

HAProxy mechanism

frontend haproxy_route_tcp_10500_10600
    bind [::]:10500-10600 v4v6
    mode tcp
    tcp-request session set-dst-port fc_dst_port
    default_backend haproxy_route_tcp_10500_10600_default_backend

backend haproxy_route_tcp_10500_10600_default_backend
    mode tcp
    server myapp-0 10.0.0.1:0

Tests

  • 11 new unit tests in tests/unit/test_haproxy_route_tcp_lib.py
  • 5 new unit tests in tests/unit/test_state.py
  • 1 new integration test test_haproxy_route_tcp_port_rangelocally verified passing

Closes #525

@swetha1654 swetha1654 requested a review from a team as a code owner June 3, 2026 08:18
@swetha1654 swetha1654 marked this pull request as draft June 3, 2026 09:14
Enable requirers to specify a port range (e.g. '10500-10600') instead
of a single port. HAProxy is configured with:
  - bind [::]:START-END v4v6  (frontend port range binding)
  - tcp-request session set-dst-port fc_dst_port  (captures incoming port)
  - server NAME IP:0  (port 0 uses the set-dst-port value, 1:1 passthrough)

Changes:
- lib/charms/haproxy/v1/haproxy_route_tcp.py (LIBPATCH=4):
  * TcpRequirerApplicationData: port made Optional, port_range field added
  * Validators: mutual exclusivity, format check, port_range+sni forbidden,
    port_range+backend_port forbidden
  * check_ports_unique: expands ranges to detect overlaps
  * provide_haproxy_route_tcp_requirements: accepts port_range parameter

- src/state/haproxy_route_tcp.py:
  * HaproxyRouteTcpServer.port: Optional[int] (None = dynamic passthrough)
  * HAProxyRouteTcpBackend: port_range servers get port=None, check=None
  * HAProxyRouteTcpFrontend: port_range field, frontend_name/bind_address/
    all_frontend_ports/default_backend_name properties updated

- src/state/haproxy_route.py:
  * parse_haproxy_route_tcp_requirers_data: groups by port_range string
  * check_tcp_http_port_conflicts: expands ranges into tcp_ports dict
  * valid_tcp_frontends: checks all ports in range for conflicts

- templates/haproxy_route_tcp.cfg.j2:
  * server rendered as IP:0 when port is None
  * bind uses frontend.bind_address (port or port range)
  * tcp-request session set-dst-port fc_dst_port added for port_range
  * TLS termination skipped for port_range frontends

- src/charm.py:
  * set_ports: expands all_frontend_ports for port_range frontends
  * publish endpoints: uses port_range string as port_str

Closes canonical#525

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@swetha1654 swetha1654 force-pushed the feat/port-range-tcp branch from 36a1a4e to 8b498cb Compare June 3, 2026 09:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add a port-range attribute to haproxy-route-tcp to allow mapping from mutliple frontend ports to multiple backend ports

1 participant