diff --git a/docs/changelog.md b/docs/changelog.md
index 25e0a6d8d..508613fab 100644
--- a/docs/changelog.md
+++ b/docs/changelog.md
@@ -1,5 +1,9 @@
# Changelog
+### 2024-12-04
+
+- Clean up corresponding OpenStack runner resources when a unit of the charm is removed.
+
### 2024-11-27
- Fix "Available Runners" dashboard panel to work for multiple flavors.
diff --git a/github-runner-manager/src-docs/reactive.consumer.md b/github-runner-manager/src-docs/reactive.consumer.md
index 132418935..f8e71f00d 100644
--- a/github-runner-manager/src-docs/reactive.consumer.md
+++ b/github-runner-manager/src-docs/reactive.consumer.md
@@ -74,7 +74,7 @@ Log the job details and acknowledge the message. If the job details are invalid,
---
-
+
## function `signal_handler`
diff --git a/src/charm.py b/src/charm.py
index 8a53c824a..7cd8785ae 100755
--- a/src/charm.py
+++ b/src/charm.py
@@ -962,7 +962,7 @@ def _on_stop(self, _: StopEvent) -> None:
if state.instance_type == InstanceType.OPENSTACK:
runner_scaler = self._get_runner_scaler(state)
- runner_scaler.flush()
+ runner_scaler.flush(FlushMode.FLUSH_BUSY)
return
runner_manager = self._get_runner_manager(state)
diff --git a/tests/unit/test_charm.py b/tests/unit/test_charm.py
index 256576a8f..7e70c6d59 100644
--- a/tests/unit/test_charm.py
+++ b/tests/unit/test_charm.py
@@ -13,6 +13,7 @@
import pytest
import yaml
from github_runner_manager.errors import ReconcileError
+from github_runner_manager.manager.runner_manager import FlushMode
from github_runner_manager.manager.runner_scaler import RunnerScaler
from github_runner_manager.types_.github import GitHubOrg, GitHubRepo, GitHubRunnerStatus
from ops.model import ActiveStatus, BlockedStatus, MaintenanceStatus, StatusBase, WaitingStatus
@@ -392,6 +393,24 @@ def test_on_reconcile_runners_reconcile_error(harness: Harness, monkeypatch: pyt
assert harness.charm.unit.status.message == ACTIVE_STATUS_RECONCILIATION_FAILED_MSG
+def test_on_stop_busy_flush(harness: Harness, monkeypatch: pytest.MonkeyPatch):
+ """
+ arrange: Set up charm with Openstack mode and runner scaler mock.
+ act: Trigger stop event.
+ assert: Runner scaler mock flushes the runners using busy mode.
+ """
+ state_mock = MagicMock()
+ state_mock.instance_type = InstanceType.OPENSTACK
+ harness.charm._setup_state = MagicMock(return_value=state_mock)
+ runner_scaler_mock = MagicMock(spec=RunnerScaler)
+ harness.charm._get_runner_scaler = MagicMock(return_value=runner_scaler_mock)
+ mock_event = MagicMock()
+
+ harness.charm._on_stop(mock_event)
+
+ runner_scaler_mock.flush.assert_called_once_with(FlushMode.FLUSH_BUSY)
+
+
@pytest.mark.parametrize(
"hook",
[