From a7a59058746fafa2d125ba51c1a9a70bc8d164b4 Mon Sep 17 00:00:00 2001 From: Alex Blank Date: Fri, 29 May 2026 17:09:43 +0200 Subject: [PATCH] fix(cloudflared): remove command override for LSIO images Problem: linuxserver/code-server already binds to 0.0.0.0 by default. Adding any command: override (--bind-addr or --host) breaks the LSIO s6 init system with 'not found' errors. Changes: - _ensure_web_bind_address(): Skip LSIO images entirely (no command override needed). If an existing override is found, remove it. - New migration 2026_05_29_remove_lsio_command_override: Removes --bind-addr and --host command overrides from both DB templates and existing instance compose files on disk for LSIO images. Quality gates: ruff clean --- .../2026_05_29_fix_code_server_bind_addr.py | 9 +- ...2026_05_29_remove_lsio_command_override.py | 109 ++++++++++++++++++ apps/api/src/api/tool_instances.py | 15 +++ 3 files changed, 129 insertions(+), 4 deletions(-) create mode 100644 apps/api/alembic/versions/2026_05_29_remove_lsio_command_override.py diff --git a/apps/api/alembic/versions/2026_05_29_fix_code_server_bind_addr.py b/apps/api/alembic/versions/2026_05_29_fix_code_server_bind_addr.py index a157b26..cbca2ca 100644 --- a/apps/api/alembic/versions/2026_05_29_fix_code_server_bind_addr.py +++ b/apps/api/alembic/versions/2026_05_29_fix_code_server_bind_addr.py @@ -5,6 +5,7 @@ Revises: 2026_05_29_fix_web_tool_bind_address Create Date: 2026-05-29 15:00:00.000000 """ + from typing import Sequence from alembic import op @@ -33,9 +34,7 @@ def upgrade() -> None: for tool_id, compose_template in result: updated = compose_template.replace( "--bind-addr 0.0.0.0:8443", "--host 0.0.0.0" - ).replace( - "--bind-addr", "--host 0.0.0.0" - ) + ).replace("--bind-addr", "--host 0.0.0.0") conn.execute( sa.text(""" @@ -46,7 +45,9 @@ def upgrade() -> None: {"compose_template": updated, "id": tool_id}, ) - print(f"Fixed code-server template ({tool_id}): replaced --bind-addr with --host") + print( + f"Fixed code-server template ({tool_id}): replaced --bind-addr with --host" + ) def downgrade() -> None: diff --git a/apps/api/alembic/versions/2026_05_29_remove_lsio_command_override.py b/apps/api/alembic/versions/2026_05_29_remove_lsio_command_override.py new file mode 100644 index 0000000..cb220bf --- /dev/null +++ b/apps/api/alembic/versions/2026_05_29_remove_lsio_command_override.py @@ -0,0 +1,109 @@ +"""Remove broken command override from LSIO code-server templates + +Revision ID: 2026_05_29_remove_lsio_command_override +Revises: 2026_05_29_fix_code_server_bind_addr +Create Date: 2026-05-29 15:05:00.000000 + +""" +from collections.abc import Sequence + +from alembic import op +import sqlalchemy as sa + +# revision identifiers, used by Alembic. +revision: str = "2026_05_29_remove_lsio_command_override" +down_revision: str | None = "2026_05_29_fix_code_server_bind_addr" +branch_labels: Sequence[str] | None = None +depends_on: Sequence[str] | None = None + + +def upgrade() -> None: + conn = op.get_bind() + + # Find code-server tool types with broken command overrides + result = conn.execute( + sa.text(""" + SELECT id, compose_template + FROM tool_types + WHERE name = 'code-server' + """) + ).fetchall() + + import yaml + from pathlib import Path + + for tool_id, compose_template in result: + try: + data = yaml.safe_load(compose_template) + except Exception: + continue + + if not data or "services" not in data: + continue + + modified = False + for svc in data["services"].values(): + image = svc.get("image", "") + if not image or "linuxserver" not in image: + continue + if "command" in svc: + cmd = svc["command"] + if "--bind-addr" in cmd or "--host" in cmd: + del svc["command"] + modified = True + + if modified: + updated = yaml.dump(data, default_flow_style=False) + conn.execute( + sa.text(""" + UPDATE tool_types + SET compose_template = :compose_template + WHERE id = :id + """), + {"compose_template": updated, "id": tool_id}, + ) + print(f"Removed broken command override from LSIO template ({tool_id})") + + # Also clean up existing instance compose files on disk + result = conn.execute( + sa.text(""" + SELECT id, compose_file_path + FROM tool_instances + WHERE compose_file_path IS NOT NULL + """) + ).fetchall() + + for instance_id, compose_path in result: + path = Path(compose_path) + if not path.exists(): + continue + try: + content = path.read_text() + data = yaml.safe_load(content) + except Exception: + continue + + if not data or "services" not in data: + continue + + modified = False + for svc in data["services"].values(): + image = svc.get("image", "") + if not image or "linuxserver" not in image: + continue + if "command" in svc: + cmd = svc["command"] + if "--bind-addr" in cmd or "--host" in cmd: + del svc["command"] + modified = True + + if modified: + path.write_text(yaml.dump(data, default_flow_style=False)) + print( + f"Removed broken command override from instance compose " + f"({instance_id})" + ) + + +def downgrade() -> None: + pass diff --git a/apps/api/src/api/tool_instances.py b/apps/api/src/api/tool_instances.py index 2dd4853..00912b8 100644 --- a/apps/api/src/api/tool_instances.py +++ b/apps/api/src/api/tool_instances.py @@ -652,6 +652,21 @@ def _ensure_web_bind_address(compose_path: str, tool_type_name: str) -> None: if not image: continue + # LSIO images already bind to 0.0.0.0 — command override breaks s6 init + if "linuxserver" in image: + existing_command = service_config.get("command", "") + if "--bind-addr" in existing_command or "--host" in existing_command: + del service_config["command"] + compose_file.write_text( + yaml.dump(compose_data, default_flow_style=False) + ) + logger.warning( + "Removed broken command override from LSIO image: %s", + existing_command, + ) + return + return + # Check if the image matches a known tool is_code_server = tool_type_name == "code-server" and ( "code-server" in image or "coder" in image