add force deletion option to pod disruption scenario (#1544)

* feat: add force pod deletion option to pod disruption scenario

Add a `force` boolean config option (default: false) that controls
whether pods are killed gracefully or forcefully. When force is true,
grace_period_seconds=0 is passed to delete_pod(), causing immediate
termination without waiting for the pod's terminationGracePeriodSeconds.

Works with both serial and parallel execution modes.

Depends on: krkn-lib feat/force-pod-delete branch

Co-authored-by: Cursor <cursoragent@cursor.com>
Signed-off-by: ddjain <darjain@redhat.com>
Co-authored-by: Cursor <cursoragent@cursor.com>

* fix: address PR review - validate force config, bump krkn-lib, add tests

- Add type validation for 'force' config to reject non-boolean values
- Bump krkn-lib to 6.1.3 (includes grace_period_seconds in delete_pod)
- Add unit tests for force deletion in serial and parallel modes

Signed-off-by: ddjain <darjain@redhat.com>
Co-authored-by: Cursor <cursoragent@cursor.com>

---------

Signed-off-by: ddjain <darjain@redhat.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Darshan Jain
2026-08-12 19:57:43 +05:30
committed by GitHub
co-authored by Cursor
parent bced12a265
commit e86ac5b802
4 changed files with 80 additions and 8 deletions
@@ -170,6 +170,66 @@ class TestKillingPodsMode(unittest.TestCase):
# Only pod2 should be deleted; pod1 is excluded
self.kubecli.delete_pod.assert_called_once_with("pod2", "ns1")
# --- force deletion tests ---
def test_force_defaults_to_false(self):
"""force defaults to False when not specified in config."""
params = InputParams({"kill": 1})
self.assertFalse(params.force)
def test_force_invalid_type_raises_value_error(self):
"""force raises ValueError when given a non-boolean value like a string."""
with self.assertRaises(ValueError) as context:
InputParams({"kill": 1, "force": "false"})
self.assertIn("Must be a boolean", str(context.exception))
def test_serial_mode_force_passes_grace_period_zero(self):
"""Serial mode with force=True calls delete_pod with grace_period_seconds=0."""
config = InputParams({"kill": 2, "execution": "serial", "force": True})
self.plugin.get_pods.return_value = [("pod1", "ns1"), ("pod2", "ns1")]
result = self.plugin.killing_pods(config, self.kubecli)
self.assertEqual(result, 0)
self.assertEqual(self.kubecli.delete_pod.call_count, 2)
self.kubecli.delete_pod.assert_any_call("pod1", "ns1", grace_period_seconds=0)
self.kubecli.delete_pod.assert_any_call("pod2", "ns1", grace_period_seconds=0)
def test_serial_mode_graceful_no_grace_period_kwarg(self):
"""Serial mode with force=False (default) calls delete_pod without grace_period_seconds."""
config = InputParams({"kill": 1, "execution": "serial", "force": False})
self.plugin.get_pods.return_value = [("pod1", "ns1")]
result = self.plugin.killing_pods(config, self.kubecli)
self.assertEqual(result, 0)
self.kubecli.delete_pod.assert_called_once_with("pod1", "ns1")
def test_parallel_mode_force_passes_grace_period_zero(self):
"""Parallel mode with force=True calls delete_pod with grace_period_seconds=0."""
config = InputParams({"kill": 2, "execution": "parallel", "force": True})
self.plugin.get_pods.return_value = [("pod1", "ns1"), ("pod2", "ns1")]
result = self.plugin.killing_pods(config, self.kubecli)
self.assertEqual(result, 0)
self.assertEqual(self.kubecli.delete_pod.call_count, 2)
self.kubecli.delete_pod.assert_any_call("pod1", "ns1", grace_period_seconds=0)
self.kubecli.delete_pod.assert_any_call("pod2", "ns1", grace_period_seconds=0)
def test_parallel_mode_graceful_no_grace_period_kwarg(self):
"""Parallel mode with force=False (default) calls delete_pod without grace_period_seconds."""
config = InputParams({"kill": 2, "execution": "parallel", "force": False})
self.plugin.get_pods.return_value = [("pod1", "ns1"), ("pod2", "ns1")]
result = self.plugin.killing_pods(config, self.kubecli)
self.assertEqual(result, 0)
self.assertEqual(self.kubecli.delete_pod.call_count, 2)
self.kubecli.delete_pod.assert_any_call("pod1", "ns1")
self.kubecli.delete_pod.assert_any_call("pod2", "ns1")
if __name__ == "__main__":
unittest.main()