Skip to content

Commit

Permalink
skip tls config when passthrough is enabled
Browse files Browse the repository at this point in the history
  • Loading branch information
IbraAoad committed Nov 28, 2024
1 parent 2f769ab commit b340c07
Show file tree
Hide file tree
Showing 2 changed files with 120 additions and 49 deletions.
85 changes: 36 additions & 49 deletions src/charm.py
Original file line number Diff line number Diff line change
Expand Up @@ -818,60 +818,47 @@ def _provide_routed_ingress(self, relation: Relation):
self._update_dynamic_config_route(relation, dct)

def _update_dynamic_config_route(self, relation: Relation, config: dict):
if "http" in config.keys():
route_config = config["http"].get("routers", {})
# we want to generate and add a new router with TLS config for each routed path.
# as we mutate the dict, we need to work on a copy
for router_name in route_config.copy().keys():
route_rule = route_config.get(router_name, {}).get("rule", "")
service_name = route_config.get(router_name, {}).get("service", "")
entrypoints = route_config.get(router_name, {}).get("entryPoints", [])
if len(entrypoints) > 0:
# if entrypoint exists, we check if it's a custom entrypoint to pass it to generated TLS config
entrypoint = entrypoints[0] if entrypoints[0] != "web" else None
else:
entrypoint = None
def _process_routes(route_config, protocol):
for router_name in list(route_config.keys()): # Work on a copy of the keys
router_details = route_config[router_name]
route_rule = router_details.get("rule", "")
service_name = router_details.get("service", "")
entrypoints = router_details.get("entryPoints", [])
tls_config = router_details.get("tls", {})

# Skip generating new routes if passthrough is already set
if tls_config.get("passthrough", False):
logger.debug(
f"Skipping TLS generation for {protocol} router {router_name} (passthrough already set)."
)
continue

entrypoint = entrypoints[0] if entrypoints else None
if protocol == "http" and entrypoint == "web":
entrypoint = None # Ignore "web" entrypoint for HTTP

if not all([router_name, route_rule, service_name]):
logger.debug("Not enough information to generate a TLS config!")
else:
config["http"]["routers"].update(
self.traefik.generate_tls_config_for_route(
router_name,
route_rule,
service_name,
# we're behind an is_ready guard, so this is guaranteed not to raise
self.external_host,
entrypoint,
)
logger.debug(
f"Not enough information to generate a TLS config for {protocol} router {router_name}!"
)
if "tcp" in config.keys():
route_config = config["tcp"].get("routers", {})
# we want to generate and add a new router with TLS config for each routed path.
# as we mutate the dict, we need to work on a copy
for router_name in route_config.copy().keys():
route_rule = route_config.get(router_name, {}).get("rule", "")
service_name = route_config.get(router_name, {}).get("service", "")
entrypoints = route_config.get(router_name, {}).get("entryPoints", [])
if len(entrypoints) > 0:
# for grpc, all entrypoints are custom
entrypoint = entrypoints[0]
else:
entrypoint = None
continue

if not all([router_name, route_rule, service_name]):
logger.debug("Not enough information to generate a TLS config!")
else:
config["tcp"]["routers"].update(
self.traefik.generate_tls_config_for_route(
router_name,
route_rule,
service_name,
# we're behind an is_ready guard, so this is guaranteed not to raise
self.external_host,
entrypoint,
)
config[protocol]["routers"].update(
self.traefik.generate_tls_config_for_route(
router_name,
route_rule,
service_name,
self.external_host,
entrypoint,
)
)

if "http" in config:
_process_routes(config["http"].get("routers", {}), protocol="http")

if "tcp" in config:
_process_routes(config["tcp"].get("routers", {}), protocol="tcp")

self._push_configurations(relation, config)

def _provide_ingress(
Expand Down
84 changes: 84 additions & 0 deletions tests/unit/test_route.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,42 @@
}
}

TCP_CONFIG_WITH_PASSTHROUGH = {
"tcp": {
"routers": {
"juju-foo-router": {
"entryPoints": ["websecure"],
"rule": "HostSNI(`*`)",
"service": "juju-foo-service",
"tls": {"passthrough": True}, # Passthrough enabled
}
},
"services": {
"juju-foo-service": {
"loadBalancer": {"servers": [{"address": "foo.testmodel-endpoints.local:8080"}]}
}
},
}
}

HTTP_CONFIG_WITH_PASSTHROUGH = {
"http": {
"routers": {
"juju-foo-router": {
"entryPoints": ["web"],
"rule": "PathPrefix(`/path`)",
"service": "juju-foo-service",
"tls": {"passthrough": True}, # Passthrough enabled
}
},
"services": {
"juju-foo-service": {
"loadBalancer": {"servers": [{"url": "http://foo.testmodel-endpoints.local:8080"}]}
}
},
}
}

CONFIG_WITH_TLS = {
"http": {
"routers": {
Expand Down Expand Up @@ -339,3 +375,51 @@ def test_static_config_updates_tcp_entrypoints(

# AND that shows up in the service ports
assert [p for p in charm._service_ports if p.port == 6767][0]


def test_tls_http_passthrough_no_tls_added(harness: Harness[TraefikIngressCharm]):
"""Ensure no TLS configuration is generated for routes with tls.passthrough."""
tr_relation_id, relation = initialize_and_setup_tr_relation(harness)
charm = harness.charm

# Update relation with the passthrough configuration
config = yaml.dump(HTTP_CONFIG_WITH_PASSTHROUGH)
harness.update_relation_data(tr_relation_id, REMOTE_APP_NAME, {"config": config})

# Verify the relation is ready and the configuration is loaded
assert charm.traefik_route.is_ready(relation)
assert charm.traefik_route.get_config(relation) == config

# Check the dynamic configuration written to the container
file = f"/opt/traefik/juju/juju_ingress_{relation.name}_{relation.id}_{relation.app.name}.yaml"
dynamic_config = yaml.safe_load(charm.container.pull(file).read())

# Ensure the passthrough configuration is preserved
assert dynamic_config == HTTP_CONFIG_WITH_PASSTHROUGH

# Check no additional TLS configurations are added
assert "juju-foo-router-tls" not in dynamic_config["http"]["routers"]


def test_tls_tcp_passthrough_no_tls_added(harness: Harness[TraefikIngressCharm]):
"""Ensure no TLS configuration is generated for routes with tls.passthrough."""
tr_relation_id, relation = initialize_and_setup_tr_relation(harness)
charm = harness.charm

# Update relation with the passthrough configuration
config = yaml.dump(TCP_CONFIG_WITH_PASSTHROUGH)
harness.update_relation_data(tr_relation_id, REMOTE_APP_NAME, {"config": config})

# Verify the relation is ready and the configuration is loaded
assert charm.traefik_route.is_ready(relation)
assert charm.traefik_route.get_config(relation) == config

# Check the dynamic configuration written to the container
file = f"/opt/traefik/juju/juju_ingress_{relation.name}_{relation.id}_{relation.app.name}.yaml"
dynamic_config = yaml.safe_load(charm.container.pull(file).read())

# Ensure the passthrough configuration is preserved
assert dynamic_config == TCP_CONFIG_WITH_PASSTHROUGH

# Check no additional TLS configurations are added
assert "juju-foo-router-tls" not in dynamic_config["tcp"]["routers"]

0 comments on commit b340c07

Please sign in to comment.