fix(cli): refuse a malformed listener instead of dropping it

This commit is contained in:
Hugo Durand
2026-09-22 16:29:33 -04:00
parent 12e16f5fcb
commit 4ea2701a29
3 changed files with 48 additions and 24 deletions
+5 -4
View File
@@ -157,10 +157,12 @@ class Listener:
namespaces: tuple[str, ...]
@classmethod
def from_resource(cls, resource: Mapping[str, object]) -> "Listener | None":
def from_resource(cls, resource: Mapping[str, object]) -> "Listener":
identifier = str(resource.get("id") or "")
if not identifier:
return None
raise HostBackendError(
"The control plane returned a listener without an id."
)
compute_config = resource.get("compute_config")
namespaces = (
compute_config.get("k8s_namespaces")
@@ -1467,8 +1469,7 @@ def _available_listeners(client: HostBackendClient) -> tuple[Listener, ...]:
resources = _call_host_backend_with_optional_tenant(
client, lambda c: c.list_listeners()
)
listeners = (Listener.from_resource(resource) for resource in resources)
return tuple(listener for listener in listeners if listener is not None)
return tuple(Listener.from_resource(resource) for resource in resources)
def _ensure_customer_registry_source(existing: ExistingDeployment) -> None:
@@ -918,24 +918,6 @@ def test_a_truncated_listener_page_says_so(deploy_project: DeployProject) -> Non
assert "first 100" in result.output
def test_a_listener_without_an_id_is_ignored(deploy_project: DeployProject) -> None:
deploy_project.control_plane.listeners = [
{"compute_id": "broken", "compute_config": {"k8s_namespaces": ["agents"]}},
LISTENER,
]
result = deploy_project.run(
"--push-to", PUSH_REPOSITORY, host_url=CLOUD_CONTROL_PLANE_URL
)
assert result.exit_code == 0, result.output
assert deploy_project.control_plane.bodies[CREATE_DEPLOYMENT]["source_config"] == {
"resource_spec": {},
"listener_id": "listener-1",
"listener_config": {"k8s_namespace": "agents"},
}
def test_a_managed_build_in_a_listener_workspace_points_at_push_to(
deploy_project: DeployProject,
) -> None:
@@ -949,3 +931,34 @@ def test_a_managed_build_in_a_listener_workspace_points_at_push_to(
assert result.exit_code != 0
assert "--push-to" in result.output
assert deploy_project.docker.verbs() == []
def test_a_managed_control_plane_without_listeners_creates_as_before(
deploy_project: DeployProject,
) -> None:
result = deploy_project.run(
"--push-to", PUSH_REPOSITORY, host_url=CLOUD_CONTROL_PLANE_URL
)
assert result.exit_code == 0, result.output
assert deploy_project.control_plane.bodies[CREATE_DEPLOYMENT]["source_config"] == {
"resource_spec": {}
}
assert deploy_project.timeline.count(LIST_LISTENERS) == 1
def test_a_listener_without_an_id_is_reported_rather_than_ignored(
deploy_project: DeployProject,
) -> None:
deploy_project.control_plane.listeners = [
{"compute_id": "broken", "compute_config": {"k8s_namespaces": ["agents"]}},
LISTENER,
]
result = deploy_project.run(
"--push-to", PUSH_REPOSITORY, host_url=CLOUD_CONTROL_PLANE_URL
)
assert result.exit_code != 0
assert "without an id" in result.output
assert deploy_project.docker.verbs() == []
@@ -960,8 +960,6 @@ class TestListener:
Listener("listener-1", "", ()),
id="only_an_id",
),
pytest.param({"compute_id": "c"}, None, id="no_id_is_not_a_listener"),
pytest.param({"id": ""}, None, id="empty_id_is_not_a_listener"),
],
)
def test_from_resource_reads_the_control_plane_shape(self, resource, expected):
@@ -1179,3 +1177,15 @@ def test_a_partial_page_without_a_match_means_the_name_is_free():
)
assert find_deployment_by_name(client, "brand-new-agent") is None
@pytest.mark.parametrize(
"resource",
[
pytest.param({"compute_id": "c"}, id="no_id"),
pytest.param({"id": ""}, id="empty_id"),
],
)
def test_a_listener_without_an_id_is_refused(resource):
with pytest.raises(HostBackendError, match="without an id"):
Listener.from_resource(resource)