From c14a8fff41290d69c4d70e8b1bf78b8506020859 Mon Sep 17 00:00:00 2001 From: "C.J. May" Date: Thu, 20 Nov 2025 02:31:30 +0000 Subject: [PATCH 01/16] use network names instead of backup container namespace --- src/restic_compose_backup/backup_runner.py | 11 ++++++++--- src/restic_compose_backup/cli.py | 2 +- src/restic_compose_backup/containers.py | 10 ++++++++++ src/tests/unit/fixtures.py | 8 ++++++++ 4 files changed, 27 insertions(+), 4 deletions(-) diff --git a/src/restic_compose_backup/backup_runner.py b/src/restic_compose_backup/backup_runner.py index f80d64b..dd6640d 100644 --- a/src/restic_compose_backup/backup_runner.py +++ b/src/restic_compose_backup/backup_runner.py @@ -12,23 +12,28 @@ def run( volumes: dict = None, environment: dict = None, labels: dict = None, - source_container_id: str = None, + network_names: set[str] = set(), ): logger.info("Starting backup container") client = utils.docker_client() - container = client.containers.run( + container = client.containers.create( image, command, labels=labels, detach=True, environment=environment + ["BACKUP_PROCESS_CONTAINER=true"], volumes=volumes, - network_mode=f"container:{source_container_id}", # reuse original container network for optional access to docker proxy working_dir=os.getcwd(), tty=True, ) + for network_name in network_names: + network = client.networks.get(network_name) + network.connect(container) + + container.start() + logger.info("Backup process container: %s", container.name) log_generator = container.logs(stdout=True, stderr=True, stream=True, follow=True) diff --git a/src/restic_compose_backup/cli.py b/src/restic_compose_backup/cli.py index e395e02..59fd437 100644 --- a/src/restic_compose_backup/cli.py +++ b/src/restic_compose_backup/cli.py @@ -173,7 +173,7 @@ def backup(config, containers: RunningContainers): command="rcb start-backup-process", volumes=volumes, environment=containers.this_container.environment, - source_container_id=containers.this_container.id, + network_names=containers.this_container.network_names, labels={ containers.backup_process_label: "True", "com.docker.compose.project": containers.project_name, diff --git a/src/restic_compose_backup/containers.py b/src/restic_compose_backup/containers.py index 55ee256..99a9794 100644 --- a/src/restic_compose_backup/containers.py +++ b/src/restic_compose_backup/containers.py @@ -35,6 +35,11 @@ def __init__(self, data: dict): self._include = self._parse_pattern(self.get_label(enums.LABEL_VOLUMES_INCLUDE)) self._exclude = self._parse_pattern(self.get_label(enums.LABEL_VOLUMES_EXCLUDE)) + # Parse network information + network_settings: dict = data.get("NetworkSettings", {}) + networks: dict = network_settings.get("Networks", {}) + self._networks = networks + @property def instance(self) -> "Container": """Container: Get a service specific subclass instance""" @@ -73,6 +78,11 @@ def service_name(self) -> str: "com.docker.compose.service", default="" ) or self.get_label("com.docker.swarm.service.name", default="") + @property + def network_names(self) -> set[str]: + """set[str]: Set of network names the container is connected to""" + return set(self._networks.keys()) + @property def backup_process_label(self) -> str: """str: The unique backup process label for this project""" diff --git a/src/tests/unit/fixtures.py b/src/tests/unit/fixtures.py index b3dd0ce..41a3bef 100644 --- a/src/tests/unit/fixtures.py +++ b/src/tests/unit/fixtures.py @@ -53,6 +53,14 @@ def wrapper(*args, **kwargs): "Status": "running", "Running": True, }, + "NetworkSettings": { + "Networks": { + f"{project}_default": { + "NetworkID": "network-id", + "IPAddress": "10.0.0.1", + } + } + }, } for container in containers ] From 8bcca1a0e2a44160f9133ae150380c39098d17c8 Mon Sep 17 00:00:00 2001 From: "C.J. May" Date: Thu, 20 Nov 2025 02:31:38 +0000 Subject: [PATCH 02/16] add podman integration tests --- .github/workflows/pr-verify.yaml | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/.github/workflows/pr-verify.yaml b/.github/workflows/pr-verify.yaml index 4f7c7e0..c9c2e6c 100644 --- a/.github/workflows/pr-verify.yaml +++ b/.github/workflows/pr-verify.yaml @@ -43,6 +43,31 @@ jobs: uv sync --locked --directory src/ uv run --directory src/ pytest -m "integration" -v + integration-tests-podman: + runs-on: ubuntu-latest + needs: unit-tests + steps: + - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 + + - name: Install Podman + run: | + sudo apt-get update + sudo apt-get -y install podman podman-compose + + - name: Configure Podman socket + run: | + systemctl --user enable --now podman.socket + export DOCKER_HOST=unix:///run/user/$UID/podman/podman.sock + + - name: Install uv + uses: astral-sh/setup-uv@d0cc045d04ccac9d8b7881df0226f9e82c39688e # v6.8.0 + + - name: Run integration tests with Podman + run: | + uv sync --locked --directory src/ + export DOCKER_HOST=unix:///run/user/$UID/podman/podman.sock + uv run --directory src/ pytest -m "integration" -v + code-quality: runs-on: ubuntu-latest steps: From d7f2f8d3c12a49324880e23a904ad2a182f6b7d8 Mon Sep 17 00:00:00 2001 From: "C.J. May" Date: Thu, 20 Nov 2025 02:40:48 +0000 Subject: [PATCH 03/16] set docker alias to podman --- .github/workflows/pr-verify.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/pr-verify.yaml b/.github/workflows/pr-verify.yaml index c9c2e6c..eb68a50 100644 --- a/.github/workflows/pr-verify.yaml +++ b/.github/workflows/pr-verify.yaml @@ -52,7 +52,7 @@ jobs: - name: Install Podman run: | sudo apt-get update - sudo apt-get -y install podman podman-compose + sudo apt-get -y install podman podman-compose podman-docker - name: Configure Podman socket run: | From 3be19e8e7eece2de927e8e050b41c1b9a08fc54f Mon Sep 17 00:00:00 2001 From: "C.J. May" Date: Thu, 20 Nov 2025 02:44:57 +0000 Subject: [PATCH 04/16] create symlink for docker socket --- .github/workflows/pr-verify.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/pr-verify.yaml b/.github/workflows/pr-verify.yaml index eb68a50..2b12703 100644 --- a/.github/workflows/pr-verify.yaml +++ b/.github/workflows/pr-verify.yaml @@ -53,6 +53,7 @@ jobs: run: | sudo apt-get update sudo apt-get -y install podman podman-compose podman-docker + ln -s /var/run/user/$(id -u)/podman/podman.sock /usr/share/docker.sock - name: Configure Podman socket run: | From 41b00679650ea1d70dc1205e302ee343d7a10fc0 Mon Sep 17 00:00:00 2001 From: "C.J. May" Date: Thu, 20 Nov 2025 02:47:02 +0000 Subject: [PATCH 05/16] add networks permission to socket-proxy --- docker-compose.test.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/docker-compose.test.yaml b/docker-compose.test.yaml index 01cb5e2..080f0b8 100644 --- a/docker-compose.test.yaml +++ b/docker-compose.test.yaml @@ -6,6 +6,7 @@ services: ALLOW_STOP: 1 CONTAINERS: 1 EXEC: 1 + NETWORKS: 1 POST: 1 VERSION: 1 read_only: true From cf9044ef848f6f432625de2cad81d4c52e0c8e28 Mon Sep 17 00:00:00 2001 From: "C.J. May" Date: Thu, 20 Nov 2025 02:55:05 +0000 Subject: [PATCH 06/16] fix symlink --- .github/workflows/pr-verify.yaml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/pr-verify.yaml b/.github/workflows/pr-verify.yaml index 2b12703..d2abd1c 100644 --- a/.github/workflows/pr-verify.yaml +++ b/.github/workflows/pr-verify.yaml @@ -53,12 +53,11 @@ jobs: run: | sudo apt-get update sudo apt-get -y install podman podman-compose podman-docker - ln -s /var/run/user/$(id -u)/podman/podman.sock /usr/share/docker.sock - name: Configure Podman socket run: | systemctl --user enable --now podman.socket - export DOCKER_HOST=unix:///run/user/$UID/podman/podman.sock + ln -s /usr/lib/systemd/user/podman.socket /var/run/docker.sock - name: Install uv uses: astral-sh/setup-uv@d0cc045d04ccac9d8b7881df0226f9e82c39688e # v6.8.0 From e9e02f7fe625a10346a9c78f60530411df098767 Mon Sep 17 00:00:00 2001 From: "C.J. May" Date: Thu, 20 Nov 2025 03:03:18 +0000 Subject: [PATCH 07/16] pass socket path to socket-proxy --- .github/workflows/pr-verify.yaml | 6 ++++-- docker-compose.test.yaml | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/.github/workflows/pr-verify.yaml b/.github/workflows/pr-verify.yaml index d2abd1c..3d6175e 100644 --- a/.github/workflows/pr-verify.yaml +++ b/.github/workflows/pr-verify.yaml @@ -42,6 +42,8 @@ jobs: run: | uv sync --locked --directory src/ uv run --directory src/ pytest -m "integration" -v + env: + DOCKER_SOCKET: /var/run/docker.sock integration-tests-podman: runs-on: ubuntu-latest @@ -57,7 +59,6 @@ jobs: - name: Configure Podman socket run: | systemctl --user enable --now podman.socket - ln -s /usr/lib/systemd/user/podman.socket /var/run/docker.sock - name: Install uv uses: astral-sh/setup-uv@d0cc045d04ccac9d8b7881df0226f9e82c39688e # v6.8.0 @@ -65,8 +66,9 @@ jobs: - name: Run integration tests with Podman run: | uv sync --locked --directory src/ - export DOCKER_HOST=unix:///run/user/$UID/podman/podman.sock uv run --directory src/ pytest -m "integration" -v + env: + DOCKER_SOCKET: /usr/lib/systemd/user/podman.socket code-quality: runs-on: ubuntu-latest diff --git a/docker-compose.test.yaml b/docker-compose.test.yaml index 080f0b8..587e951 100644 --- a/docker-compose.test.yaml +++ b/docker-compose.test.yaml @@ -13,7 +13,7 @@ services: tmpfs: - /run volumes: - - /var/run/docker.sock:/var/run/docker.sock:ro + - ${DOCKER_SOCKET}:/var/run/docker.sock:ro restart: unless-stopped networks: - backup From f772dd4f9531e40863f0b110ac0e7de7081728ae Mon Sep 17 00:00:00 2001 From: "C.J. May" Date: Thu, 20 Nov 2025 03:15:06 +0000 Subject: [PATCH 08/16] fix socket permissions --- .github/workflows/pr-verify.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/pr-verify.yaml b/.github/workflows/pr-verify.yaml index 3d6175e..d6bda4c 100644 --- a/.github/workflows/pr-verify.yaml +++ b/.github/workflows/pr-verify.yaml @@ -59,6 +59,7 @@ jobs: - name: Configure Podman socket run: | systemctl --user enable --now podman.socket + chmod 666 /usr/lib/systemd/user/podman.socket - name: Install uv uses: astral-sh/setup-uv@d0cc045d04ccac9d8b7881df0226f9e82c39688e # v6.8.0 From ae2f20e969fbd550f903b1bf7201ddd4f3bf5505 Mon Sep 17 00:00:00 2001 From: "C.J. May" Date: Thu, 20 Nov 2025 03:20:15 +0000 Subject: [PATCH 09/16] fix podman socket path --- .github/workflows/pr-verify.yaml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/pr-verify.yaml b/.github/workflows/pr-verify.yaml index d6bda4c..202d788 100644 --- a/.github/workflows/pr-verify.yaml +++ b/.github/workflows/pr-verify.yaml @@ -59,7 +59,6 @@ jobs: - name: Configure Podman socket run: | systemctl --user enable --now podman.socket - chmod 666 /usr/lib/systemd/user/podman.socket - name: Install uv uses: astral-sh/setup-uv@d0cc045d04ccac9d8b7881df0226f9e82c39688e # v6.8.0 @@ -69,7 +68,7 @@ jobs: uv sync --locked --directory src/ uv run --directory src/ pytest -m "integration" -v env: - DOCKER_SOCKET: /usr/lib/systemd/user/podman.socket + DOCKER_SOCKET: /run/podman/podman.sock code-quality: runs-on: ubuntu-latest From 5fde8145108be2d52b36211fd9eba256f41bdd66 Mon Sep 17 00:00:00 2001 From: "C.J. May" Date: Thu, 20 Nov 2025 03:22:02 +0000 Subject: [PATCH 10/16] use rootful podman --- .github/workflows/pr-verify.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/pr-verify.yaml b/.github/workflows/pr-verify.yaml index 202d788..366971a 100644 --- a/.github/workflows/pr-verify.yaml +++ b/.github/workflows/pr-verify.yaml @@ -58,7 +58,7 @@ jobs: - name: Configure Podman socket run: | - systemctl --user enable --now podman.socket + sudo systemctl enable --now podman.socket - name: Install uv uses: astral-sh/setup-uv@d0cc045d04ccac9d8b7881df0226f9e82c39688e # v6.8.0 From 4f482268c5cbe9d750b4969f9004ea5b7d22ff44 Mon Sep 17 00:00:00 2001 From: "C.J. May" Date: Thu, 20 Nov 2025 03:35:13 +0000 Subject: [PATCH 11/16] podman-docker symlinks docker.sock --- .github/workflows/pr-verify.yaml | 4 ---- docker-compose.test.yaml | 2 +- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/.github/workflows/pr-verify.yaml b/.github/workflows/pr-verify.yaml index 366971a..b512f3c 100644 --- a/.github/workflows/pr-verify.yaml +++ b/.github/workflows/pr-verify.yaml @@ -42,8 +42,6 @@ jobs: run: | uv sync --locked --directory src/ uv run --directory src/ pytest -m "integration" -v - env: - DOCKER_SOCKET: /var/run/docker.sock integration-tests-podman: runs-on: ubuntu-latest @@ -67,8 +65,6 @@ jobs: run: | uv sync --locked --directory src/ uv run --directory src/ pytest -m "integration" -v - env: - DOCKER_SOCKET: /run/podman/podman.sock code-quality: runs-on: ubuntu-latest diff --git a/docker-compose.test.yaml b/docker-compose.test.yaml index 587e951..080f0b8 100644 --- a/docker-compose.test.yaml +++ b/docker-compose.test.yaml @@ -13,7 +13,7 @@ services: tmpfs: - /run volumes: - - ${DOCKER_SOCKET}:/var/run/docker.sock:ro + - /var/run/docker.sock:/var/run/docker.sock:ro restart: unless-stopped networks: - backup From d3b7bf3019fe2ceef5190a34c650650b57243f09 Mon Sep 17 00:00:00 2001 From: "C.J. May" Date: Wed, 19 Nov 2025 22:30:25 -0600 Subject: [PATCH 12/16] usermode podman --- .github/workflows/pr-verify.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/pr-verify.yaml b/.github/workflows/pr-verify.yaml index b512f3c..a3334f7 100644 --- a/.github/workflows/pr-verify.yaml +++ b/.github/workflows/pr-verify.yaml @@ -56,7 +56,7 @@ jobs: - name: Configure Podman socket run: | - sudo systemctl enable --now podman.socket + systemctl --user enable --now podman.socket - name: Install uv uses: astral-sh/setup-uv@d0cc045d04ccac9d8b7881df0226f9e82c39688e # v6.8.0 From 7671fa6f063d6eba0b07839c40f3f8c7dd6fab3b Mon Sep 17 00:00:00 2001 From: "C.J. May" Date: Wed, 19 Nov 2025 22:30:35 -0600 Subject: [PATCH 13/16] specify registry for podman --- src/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Dockerfile b/src/Dockerfile index 8725bc9..60a7fa5 100644 --- a/src/Dockerfile +++ b/src/Dockerfile @@ -1,6 +1,6 @@ FROM ghcr.io/astral-sh/uv:0.9.10 AS uv-builder -FROM restic/restic:0.18.1 +FROM docker.io/restic/restic:0.18.1 RUN apk update && apk add dcron From f1339ed69f2f6b834a0475524bb1dbeddf9add32 Mon Sep 17 00:00:00 2001 From: "C.J. May" Date: Wed, 19 Nov 2025 22:38:10 -0600 Subject: [PATCH 14/16] fix docker socket symlink --- .github/workflows/pr-verify.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/pr-verify.yaml b/.github/workflows/pr-verify.yaml index a3334f7..2bb817a 100644 --- a/.github/workflows/pr-verify.yaml +++ b/.github/workflows/pr-verify.yaml @@ -57,6 +57,8 @@ jobs: - name: Configure Podman socket run: | systemctl --user enable --now podman.socket + sudo rm -rf /var/run/docker.sock + sudo ln -s /run/user/$(id -u)/podman/podman.sock /var/run/docker.sock - name: Install uv uses: astral-sh/setup-uv@d0cc045d04ccac9d8b7881df0226f9e82c39688e # v6.8.0 From ef12d6f8ef69e452e1e43ef8081cede8d94154f3 Mon Sep 17 00:00:00 2001 From: "C.J. May" Date: Wed, 19 Nov 2025 23:54:10 -0600 Subject: [PATCH 15/16] make multi project fixture comaptible with podman --- src/tests/integration/conftest.py | 30 ++++++++++++++++++++++++++---- 1 file changed, 26 insertions(+), 4 deletions(-) diff --git a/src/tests/integration/conftest.py b/src/tests/integration/conftest.py index 7af4b44..0dc6832 100644 --- a/src/tests/integration/conftest.py +++ b/src/tests/integration/conftest.py @@ -487,18 +487,40 @@ def backup_container_with_multi_project( # Remove the old container backup_cont.remove() + # Parse volumes from Binds format to volumes dict format + # Binds format: ["/host/path:/container/path:ro"] + # Volumes format: {"/host/path": {"bind": "/container/path", "mode": "ro"}} + volumes_dict = {} + for bind in host_config.get("Binds", []): + parts = bind.split(":") + if len(parts) >= 2: + host_path = parts[0] + container_path = parts[1] + mode = parts[2] if len(parts) > 2 else "rw" + volumes_dict[host_path] = {"bind": container_path, "mode": mode} + + # Get network names + networks = list(container_info["NetworkSettings"]["Networks"].keys()) + # Create a new container with the updated environment + # which is needed for the backup container to identify itself new_container = docker_client.containers.create( config["Image"], environment=env_list, - volumes=host_config.get("Binds", []), - network=list(container_info["NetworkSettings"]["Networks"].keys())[0] - if container_info["NetworkSettings"]["Networks"] - else None, + volumes=volumes_dict, name=container_info["Name"].strip("/"), labels=config.get("Labels", {}), ) + # Connect to networks after creation (more compatible with Podman) + for network_name in networks: + try: + network = docker_client.networks.get(network_name) + network.connect(new_container) + except Exception: + # If network connection fails, continue (may already be connected) + pass + # Start the new container new_container.start() From 80f3f8909406cedf4694c1ef28846e12f209216b Mon Sep 17 00:00:00 2001 From: "C.J. May" Date: Fri, 2 Jan 2026 13:19:22 -0600 Subject: [PATCH 16/16] update integration test for podman compatibility --- src/tests/integration/conftest.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/tests/integration/conftest.py b/src/tests/integration/conftest.py index 0dc6832..616f561 100644 --- a/src/tests/integration/conftest.py +++ b/src/tests/integration/conftest.py @@ -504,22 +504,23 @@ def backup_container_with_multi_project( # Create a new container with the updated environment # which is needed for the backup container to identify itself + # Note: We don't set hostname - Docker/Podman will set it to the container ID new_container = docker_client.containers.create( config["Image"], + command=config.get("Cmd"), environment=env_list, volumes=volumes_dict, name=container_info["Name"].strip("/"), labels=config.get("Labels", {}), + detach=True, + working_dir=config.get("WorkingDir"), + entrypoint=config.get("Entrypoint"), ) # Connect to networks after creation (more compatible with Podman) for network_name in networks: - try: - network = docker_client.networks.get(network_name) - network.connect(new_container) - except Exception: - # If network connection fails, continue (may already be connected) - pass + network = docker_client.networks.get(network_name) + network.connect(new_container) # Start the new container new_container.start()