Skip to content

Commit b5a8ef0

Browse files
Fault injector boilerplate (#3749)
* Add fault injector client for Redis Enterprise testing This commit introduces a comprehensive fault injector client that provides a Python interface for interacting with Redis Enterprise fault injection services. The client supports: - Multiple action types (DMC restart, failover, reshard, network failure) - Direct rladmin command execution - Sequence of actions for complex testing scenarios - Full HTTP API integration with proper error handling - Debug output for troubleshooting test scenarios The fault injector client enables automated testing of Redis Enterprise cluster resilience and failover scenarios, supporting the CAE client testing framework approach for real enterprise server integration. * Apply linting fixes to fault injector client - Fixed whitespace in blank lines (W293) - Added trailing newline at end of file (W292) - Applied ruff formatting for consistent code style: * Improved line breaks for long function signatures * Consistent quote style (single to double quotes) * Proper parameter alignment and indentation All linting checks now pass: - ruff check: ✅ All checks passed - ruff format: ✅ Code properly formatted - vulture: ✅ No dead code detected * take care of review issues
1 parent d24c9c9 commit b5a8ef0

File tree

2 files changed

+105
-0
lines changed

2 files changed

+105
-0
lines changed

tests/test_scenario/__init__.py

Whitespace-only changes.
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
import json
2+
import urllib.request
3+
from typing import Dict, Any, Optional, Union
4+
from enum import Enum
5+
6+
7+
class ActionType(str, Enum):
8+
DMC_RESTART = "dmc_restart"
9+
FAILOVER = "failover"
10+
RESHARD = "reshard"
11+
SEQUENCE_OF_ACTIONS = "sequence_of_actions"
12+
NETWORK_FAILURE = "network_failure"
13+
EXECUTE_RLUTIL_COMMAND = "execute_rlutil_command"
14+
EXECUTE_RLADMIN_COMMAND = "execute_rladmin_command"
15+
16+
17+
class RestartDmcParams:
18+
def __init__(self, bdb_id: str):
19+
self.bdb_id = bdb_id
20+
21+
def to_dict(self) -> Dict[str, str]:
22+
return {"bdb_id": self.bdb_id}
23+
24+
25+
class ActionRequest:
26+
def __init__(
27+
self,
28+
action_type: ActionType,
29+
parameters: Union[Dict[str, Any], RestartDmcParams],
30+
):
31+
self.type = action_type
32+
self.parameters = parameters
33+
34+
def to_dict(self) -> Dict[str, Any]:
35+
return {
36+
"type": self.type.value, # Use the string value of the enum
37+
"parameters": self.parameters.to_dict()
38+
if isinstance(self.parameters, RestartDmcParams)
39+
else self.parameters,
40+
}
41+
42+
43+
class FaultInjectorClient:
44+
def __init__(self, base_url: str):
45+
self.base_url = base_url.rstrip("/")
46+
47+
def _make_request(
48+
self, method: str, path: str, data: Optional[Dict] = None
49+
) -> Dict[str, Any]:
50+
url = f"{self.base_url}{path}"
51+
headers = {"Content-Type": "application/json"} if data else {}
52+
53+
request_data = json.dumps(data).encode("utf-8") if data else None
54+
55+
request = urllib.request.Request(
56+
url, method=method, data=request_data, headers=headers
57+
)
58+
59+
try:
60+
with urllib.request.urlopen(request) as response:
61+
return json.loads(response.read().decode("utf-8"))
62+
except urllib.error.HTTPError as e:
63+
if e.code == 422:
64+
error_body = json.loads(e.read().decode("utf-8"))
65+
raise ValueError(f"Validation Error: {error_body}")
66+
raise
67+
68+
def list_actions(self) -> Dict[str, Any]:
69+
"""List all available actions"""
70+
return self._make_request("GET", "/action")
71+
72+
def trigger_action(self, action_request: ActionRequest) -> Dict[str, Any]:
73+
"""Trigger a new action"""
74+
request_data = action_request.to_dict()
75+
return self._make_request("POST", "/action", request_data)
76+
77+
def get_action_status(self, action_id: str) -> Dict[str, Any]:
78+
"""Get the status of a specific action"""
79+
return self._make_request("GET", f"/action/{action_id}")
80+
81+
def execute_rladmin_command(
82+
self, command: str, bdb_id: str = None
83+
) -> Dict[str, Any]:
84+
"""Execute rladmin command directly as string"""
85+
url = f"{self.base_url}/rladmin"
86+
87+
# The fault injector expects the raw command string
88+
command_string = f"rladmin {command}"
89+
if bdb_id:
90+
command_string = f"rladmin -b {bdb_id} {command}"
91+
92+
headers = {"Content-Type": "text/plain"}
93+
94+
request = urllib.request.Request(
95+
url, method="POST", data=command_string.encode("utf-8"), headers=headers
96+
)
97+
98+
try:
99+
with urllib.request.urlopen(request) as response:
100+
return json.loads(response.read().decode("utf-8"))
101+
except urllib.error.HTTPError as e:
102+
if e.code == 422:
103+
error_body = json.loads(e.read().decode("utf-8"))
104+
raise ValueError(f"Validation Error: {error_body}")
105+
raise

0 commit comments

Comments
 (0)