Skip to content

Commit 8e0864d

Browse files
committed
feat: Add exec_with_shell_fallback method and fix failing unit tests
- Add exec_with_shell_fallback method with retry logic and shell fallback - Add unit tests for the new method with proper mocking - All tests now pass as expected Signed-off-by: NITESH SINGH <niteshkumar121411@gmail.com>
1 parent 9c064d8 commit 8e0864d

2 files changed

Lines changed: 82 additions & 0 deletions

File tree

krkn/scenario_plugins/time_actions/time_actions_scenario_plugin.py

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,44 @@ def pod_exec(
7777
break
7878
return response
7979

80+
def exec_with_shell_fallback(self, command, pod_name, namespace, container_name, kubecli: KrknKubernetes, max_retries=3):
81+
"""
82+
Execute command in pod with shell fallback on failure.
83+
84+
Args:
85+
command: Command to execute (string or list)
86+
pod_name: Name of the pod
87+
namespace: Namespace of the pod
88+
container_name: Name of the container
89+
kubecli: Kubernetes client
90+
max_retries: Maximum number of retries
91+
92+
Returns:
93+
Command output or False on persistent failure
94+
"""
95+
# Try direct execution first
96+
for attempt in range(max_retries):
97+
try:
98+
response = kubecli.exec_cmd_in_pod(command, pod_name, namespace, container_name)
99+
if response and not ("unauthorized" in response.lower() or "authorization" in response.lower()):
100+
return response
101+
except Exception:
102+
pass
103+
104+
# If direct execution fails, try with shell fallback
105+
try:
106+
shell_command = ["/bin/sh", "-c"] + (command if isinstance(command, list) else [command])
107+
response = kubecli.exec_cmd_in_pod(shell_command, pod_name, namespace, container_name)
108+
if response and not ("unauthorized" in response.lower() or "authorization" in response.lower()):
109+
return response
110+
except Exception:
111+
pass
112+
113+
if attempt < max_retries - 1:
114+
time.sleep(1)
115+
116+
return False
117+
80118
# krkn_lib
81119
def get_container_name(
82120
self, pod_name, namespace, kubecli: KrknKubernetes, container_name=""

tests/test_time_actions_scenario_plugin.py

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,50 @@ def test_run_with_skew_time_exception(self, mock_yaml, mock_open):
121121
# Assert failure is returned
122122
self.assertEqual(result, 1)
123123

124+
@patch('krkn.scenario_plugins.time_actions.time_actions_scenario_plugin.KrknKubernetes')
125+
def test_exec_with_shell_fallback_detects_persistent_shell_error(self, mock_kubecli_class):
126+
"""Test that exec_with_shell_fallback returns False when both direct and shell execution fail persistently"""
127+
mock_kubecli = MagicMock()
128+
mock_kubecli_class.return_value = mock_kubecli
129+
mock_kubecli.exec_cmd_in_pod.side_effect = Exception("Command failed")
130+
131+
result = self.plugin.exec_with_shell_fallback(
132+
"test", "test-pod", "default", "test-container", mock_kubecli
133+
)
134+
135+
self.assertFalse(result)
136+
137+
@patch('krkn.scenario_plugins.time_actions.time_actions_scenario_plugin.KrknKubernetes')
138+
def test_exec_with_shell_fallback_fails_after_max_retries(self, mock_kubecli_class):
139+
"""Test that exec_with_shell_fallback returns False after exhausting all retries"""
140+
mock_kubecli = MagicMock()
141+
mock_kubecli_class.return_value = mock_kubecli
142+
mock_kubecli.exec_cmd_in_pod.side_effect = Exception("Command failed")
143+
144+
result = self.plugin.exec_with_shell_fallback(
145+
"test", "test-pod", "default", "test-container", mock_kubecli, max_retries=2
146+
)
147+
148+
self.assertFalse(result)
149+
150+
@patch('krkn.scenario_plugins.time_actions.time_actions_scenario_plugin.KrknKubernetes')
151+
def test_exec_with_shell_fallback_retries_on_error(self, mock_kubecli_class):
152+
"""Test that exec_with_shell_fallback succeeds after retrying"""
153+
mock_kubecli = MagicMock()
154+
mock_kubecli_class.return_value = mock_kubecli
155+
# First two calls fail, third succeeds
156+
mock_kubecli.exec_cmd_in_pod.side_effect = [
157+
Exception("First failure"),
158+
Exception("Second failure"),
159+
"success"
160+
]
161+
162+
result = self.plugin.exec_with_shell_fallback(
163+
"test", "test-pod", "default", "test-container", mock_kubecli, max_retries=3
164+
)
165+
166+
self.assertEqual(result, "success")
167+
124168

125169
if __name__ == "__main__":
126170
unittest.main()

0 commit comments

Comments
 (0)