Skip to content

Commit

Permalink
feat(purge): Updating purge to only stop relevant containers (#176)
Browse files Browse the repository at this point in the history
* feat(purge): Updating purge to only stop relevant containers

* Adding constant

* Adding more test coverage

* Adding more test coverage

* Adding more test coverage
  • Loading branch information
IanWoodard authored Dec 12, 2024
1 parent ade1b03 commit 0f26f0b
Show file tree
Hide file tree
Showing 6 changed files with 331 additions and 93 deletions.
23 changes: 10 additions & 13 deletions devservices/commands/purge.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,13 @@
from argparse import Namespace

from devservices.constants import DEVSERVICES_CACHE_DIR
from devservices.constants import DEVSERVICES_ORCHESTRATOR_LABEL
from devservices.constants import DOCKER_NETWORK_NAME
from devservices.exceptions import DockerDaemonNotRunningError
from devservices.exceptions import DockerError
from devservices.utils.console import Console
from devservices.utils.console import Status
from devservices.utils.docker import stop_all_running_containers
from devservices.utils.docker import stop_matching_containers
from devservices.utils.state import State


Expand All @@ -25,14 +27,6 @@ def purge(_args: Namespace) -> None:
"""Purge the local devservices cache."""
console = Console()

# Prompt the user to stop all running containers
should_stop_containers = console.confirm(
"Warning: Purging stops all running containers and clears devservices state. Would you like to continue?"
)
if not should_stop_containers:
console.warning("Purge canceled")
return

if os.path.exists(DEVSERVICES_CACHE_DIR):
try:
shutil.rmtree(DEVSERVICES_CACHE_DIR)
Expand All @@ -42,13 +36,16 @@ def purge(_args: Namespace) -> None:
state = State()
state.clear_state()
with Status(
lambda: console.warning("Stopping all running containers"),
lambda: console.success("All running containers have been stopped"),
lambda: console.warning("Stopping all running devservices containers"),
lambda: console.success("All running devservices containers have been stopped"),
):
try:
stop_all_running_containers()
stop_matching_containers(DEVSERVICES_ORCHESTRATOR_LABEL, should_remove=True)
except DockerDaemonNotRunningError:
console.warning("The docker daemon not running, no containers to stop")
console.warning("The docker daemon is not running, no containers to stop")
except DockerError as e:
console.failure(f"Failed to stop running devservices containers {e.stderr}")
exit(1)

console.warning("Removing any devservices networks")
devservices_networks = (
Expand Down
1 change: 1 addition & 0 deletions devservices/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
DEVSERVICES_LOCAL_DIR = os.path.expanduser("~/.local/share/sentry-devservices")
DEVSERVICES_DEPENDENCIES_CACHE_DIR = os.path.join(DEVSERVICES_CACHE_DIR, "dependencies")
DEVSERVICES_DEPENDENCIES_CACHE_DIR_KEY = "DEVSERVICES_DEPENDENCIES_CACHE_DIR"
DEVSERVICES_ORCHESTRATOR_LABEL = "orchestrator=devservices"
STATE_DB_FILE = os.path.join(DEVSERVICES_LOCAL_DIR, "state")
DOCKER_COMPOSE_COMMAND_LENGTH = 7

Expand Down
10 changes: 8 additions & 2 deletions devservices/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,8 @@ class DockerComposeInstallationError(BinaryInstallError):
pass


class DockerComposeError(Exception):
"""Base class for Docker Compose related errors."""
class DockerError(Exception):
"""Base class for Docker related errors."""

def __init__(self, command: str, returncode: int, stdout: str, stderr: str):
self.command = command
Expand All @@ -67,6 +67,12 @@ def __init__(self, command: str, returncode: int, stdout: str, stderr: str):
self.stderr = stderr


class DockerComposeError(DockerError):
"""Base class for Docker Compose related errors."""

pass


class ModeDoesNotExistError(Exception):
"""Raised when a mode does not exist."""

Expand Down
80 changes: 66 additions & 14 deletions devservices/utils/docker.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import subprocess

from devservices.exceptions import DockerDaemonNotRunningError
from devservices.exceptions import DockerError


def check_docker_daemon_running() -> None:
Expand All @@ -18,19 +19,70 @@ def check_docker_daemon_running() -> None:
raise DockerDaemonNotRunningError from e


def stop_all_running_containers() -> None:
def get_matching_containers(label: str) -> list[str]:
"""
Returns a list of container IDs with the given label
"""
check_docker_daemon_running()
running_containers = (
subprocess.check_output(["docker", "ps", "-q"], stderr=subprocess.DEVNULL)
.decode()
.strip()
.splitlines()
)
if len(running_containers) == 0:
try:
return (
subprocess.check_output(
[
"docker",
"ps",
"-q",
"--filter",
f"label={label}",
],
stderr=subprocess.DEVNULL,
)
.decode()
.strip()
.splitlines()
)
except subprocess.CalledProcessError as e:
raise DockerError(
command=f"docker ps -q --filter label={label}",
returncode=e.returncode,
stdout=e.stdout,
stderr=e.stderr,
) from e


def stop_matching_containers(label: str, should_remove: bool = False) -> None:
"""
Stops all containers with the given label.
If should_remove is True, the containers will be removed.
"""
matching_containers = get_matching_containers(label)
if len(matching_containers) == 0:
return
subprocess.run(
["docker", "stop"] + running_containers,
check=True,
stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL,
)
try:
subprocess.run(
["docker", "stop"] + matching_containers,
check=True,
stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL,
)
except subprocess.CalledProcessError as e:
raise DockerError(
command=f"docker stop {' '.join(matching_containers)}",
returncode=e.returncode,
stdout=e.stdout,
stderr=e.stderr,
) from e
if should_remove:
try:
subprocess.run(
["docker", "rm"] + matching_containers,
check=True,
stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL,
)
except subprocess.CalledProcessError as e:
raise DockerError(
command=f"docker rm {' '.join(matching_containers)}",
returncode=e.returncode,
stdout=e.stdout,
stderr=e.stderr,
) from e
Loading

0 comments on commit 0f26f0b

Please sign in to comment.