Skip to content

Commit cc5ebc0

Browse files
markkasaboskiMark Kasaboskixvaier
authored
Improve handling for errors while fetching full events (#94)
Co-authored-by: Mark Kasaboski <mark.kasaboski@gmail.com> Co-authored-by: Xavier Lavallee <xavier.lavallee@gmail.com>
1 parent b9b4e60 commit cc5ebc0

6 files changed

Lines changed: 282 additions & 156 deletions

File tree

packages/flare/bin/flare.py

Lines changed: 26 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -46,12 +46,18 @@ def get_flare_api_client(
4646

4747

4848
class FlareAPI(AuthBase):
49-
def __init__(self, *, api_key: str, tenant_id: Optional[int] = None) -> None:
49+
def __init__(
50+
self,
51+
*,
52+
api_key: str,
53+
tenant_id: Optional[int] = None,
54+
logger: Logger,
55+
) -> None:
5056
self.flare_client = get_flare_api_client(
5157
api_key=api_key,
5258
tenant_id=tenant_id,
5359
)
54-
self.logger = Logger(class_name=__file__)
60+
self.logger = logger
5561

5662
def fetch_feed_events(
5763
self,
@@ -76,7 +82,7 @@ def fetch_feed_events(
7682
event = self._fetch_full_event_from_uid(
7783
uid=event["metadata"]["uid"]
7884
)
79-
time.sleep(1)
85+
time.sleep(1) # Don't hit rate limit
8086
yield (event, next_token)
8187

8288
def _fetch_event_feed_metadata(
@@ -115,10 +121,23 @@ def _fetch_event_feed_metadata(
115121
time.sleep(1)
116122

117123
def _fetch_full_event_from_uid(self, *, uid: str) -> dict:
118-
event_response = self.flare_client.get(url=f"/firework/v2/activities/{uid}")
119-
event = event_response.json()["activity"]
120-
self.logger.debug(event)
121-
return event
124+
number_of_retries = 3
125+
for current_try in range(number_of_retries):
126+
try:
127+
event_response = self.flare_client.get(
128+
url=f"/firework/v2/activities/{uid}"
129+
)
130+
event_response.raise_for_status()
131+
except Exception as e:
132+
time.sleep(1)
133+
self.logger.info(
134+
f"Failed to fetch event {current_try + 1}/{number_of_retries} retries: {e}"
135+
)
136+
continue
137+
return event_response.json()["activity"]
138+
raise Exception(
139+
f"failed to fetch full event data for {uid} after {number_of_retries} tries"
140+
)
122141

123142
def fetch_api_key_validation(self) -> requests.Response:
124143
return self.flare_client.get(

packages/flare/bin/flare_external_requests.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,14 @@
2121

2222
class FlareValidateApiKey(splunk.rest.BaseRestHandler):
2323
def handle_POST(self) -> None:
24+
logger = Logger(class_name=__file__)
2425
payload = self.request["payload"]
2526
params = parse.parse_qs(payload)
2627

2728
if "apiKey" not in params:
2829
raise Exception("API Key is required")
2930

30-
flare_api = FlareAPI(api_key=params["apiKey"][0])
31+
flare_api = FlareAPI(api_key=params["apiKey"][0], logger=logger)
3132
flare_api.fetch_api_key_validation()
3233
self.response.setHeader("Content-Type", "application/json")
3334
self.response.write(json.dumps({}))
@@ -42,7 +43,7 @@ def handle_POST(self) -> None:
4243
if "apiKey" not in params:
4344
raise Exception("API Key is required")
4445

45-
flare_api = FlareAPI(api_key=params["apiKey"][0])
46+
flare_api = FlareAPI(api_key=params["apiKey"][0], logger=logger)
4647
response = flare_api.fetch_tenants()
4748
response_json = response.json()
4849
logger.debug(f"FlareUserTenants: {response_json}")
@@ -59,7 +60,7 @@ def handle_POST(self) -> None:
5960
if "apiKey" not in params:
6061
raise Exception("API Key is required")
6162

62-
flare_api = FlareAPI(api_key=params["apiKey"][0])
63+
flare_api = FlareAPI(api_key=params["apiKey"][0], logger=logger)
6364
response = flare_api.fetch_filters_severity()
6465
response_json = response.json()
6566
logger.debug(f"FlareSeverityFilters: {response_json}")
@@ -76,7 +77,7 @@ def handle_POST(self) -> None:
7677
if "apiKey" not in params:
7778
raise Exception("API Key is required")
7879

79-
flare_api = FlareAPI(api_key=params["apiKey"][0])
80+
flare_api = FlareAPI(api_key=params["apiKey"][0], logger=logger)
8081
response = flare_api.fetch_filters_source_type()
8182
response_json = response.json()
8283
logger.debug(f"FlareSourceTypeFilters: {response_json}")

packages/flare/tests/bin/conftest.py

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,11 @@
1111

1212

1313
sys.path.insert(0, os.path.join(os.path.dirname(__file__), "../../bin"))
14+
sys.path.insert(0, os.path.join(os.path.dirname(__file__), "../../bin/vendor"))
1415
from data_store import ConfigDataStore
16+
from flare import FlareAPI
17+
from logger import Logger
18+
from vendor.splunklib.client import StoragePasswords
1519

1620

1721
class FakeStoragePassword:
@@ -34,26 +38,29 @@ def clear_password(self) -> str:
3438
return self._state["clear_password"]
3539

3640

37-
class FakeStoragePasswords:
41+
class FakeStoragePasswords(StoragePasswords):
3842
def __init__(self, passwords: List[FakeStoragePassword]) -> None:
3943
self._passwords = passwords
4044

4145
def list(self) -> List[FakeStoragePassword]:
4246
return self._passwords
4347

4448

45-
class FakeLogger:
49+
class FakeLogger(Logger):
4650
def __init__(self) -> None:
51+
super().__init__(class_name="Logger")
4752
self.messages: List[str] = []
4853

54+
self._mock = mock.MagicMock(spec=Logger)
55+
4956
def info(self, message: str) -> None:
5057
self.messages.append(f"INFO: {message}")
5158

5259
def error(self, message: str) -> None:
5360
self.messages.append(f"ERROR: {message}")
5461

5562

56-
class FakeFlareAPI:
63+
class FakeFlareAPI(FlareAPI):
5764
def __init__(self, api_key: str, tenant_id: int) -> None:
5865
pass
5966

@@ -92,7 +99,7 @@ def storage_passwords(request: pytest.FixtureRequest) -> FakeStoragePasswords:
9299

93100

94101
@pytest.fixture
95-
def logger() -> FakeLogger:
102+
def logger() -> Logger:
96103
return FakeLogger()
97104

98105

@@ -121,3 +128,9 @@ def data_store(mock_env: None) -> Generator[ConfigDataStore, None, None]:
121128
store = ConfigDataStore()
122129
store._commit = lambda: None
123130
yield store
131+
132+
133+
@pytest.fixture
134+
def disable_sleep() -> Generator[None, None, None]:
135+
with mock.patch("time.sleep", return_value=None):
136+
yield

0 commit comments

Comments
 (0)