diff --git a/src/appsignal/client.py b/src/appsignal/client.py index 8f40272..45b522b 100644 --- a/src/appsignal/client.py +++ b/src/appsignal/client.py @@ -1,5 +1,9 @@ from __future__ import annotations +import os +import signal +import subprocess +import time from typing import TYPE_CHECKING from . import internal_logger as logger @@ -48,6 +52,21 @@ def start(self) -> None: else: logger.info("AppSignal not starting: no active config found") + def stop_agent_processes(self) -> None: + logger.info("Killing all AppSignal agent processes") + + processes = subprocess.Popen(["ps", "-A"], stdout=subprocess.PIPE) + out, _ = processes.communicate() + processes_found = 0 + + for line in out.splitlines(): + if b"appsignal-" in line: + pid = int(line.split(None, 1)[0]) + os.kill(pid, signal.SIGTERM) + processes_found += 1 + + logger.info(f"AppSignal stopped {processes_found} agent processes") + def _start_probes(self) -> None: if self._config.option("enable_minutely_probes"): start_probes() diff --git a/tests/test_client.py b/tests/test_client.py index 0a4b69f..0ecf787 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -1,6 +1,8 @@ from __future__ import annotations import os +import signal +from unittest.mock import patch from appsignal.agent import agent from appsignal.client import Client @@ -98,3 +100,21 @@ def test_client_inactive(): os.environ.get("OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SERVER_REQUEST") is None ) + + +@patch("subprocess.Popen.communicate") +@patch("os.kill") +def test_client_stop(mock_kill, mock_communicate): + mock_communicate.return_value = ( + b"123 ?? 12:00.00 /bin/appsignal-agent\n124 ?? 12:00.00 /bin/appsignal-agent\n", + None, + ) + mock_kill.return_value = None + + client = Client(active=True, name="MyApp", push_api_key="0000-0000-0000-0000") + client.start() + + client.stop_agent_processes() + + mock_kill.assert_called(123, signal.SIGTERM) + mock_kill.assert_called(124, signal.SIGTERM)