Skip to content

Commit 58dcd1d

Browse files
new: Add handling for failed events in EventPoller(...).wait_for_next_event_finished(...) (#384)
* Add handling for failed events in EventPoller * oops * oops
1 parent fd151d5 commit 58dcd1d

2 files changed

Lines changed: 81 additions & 1 deletion

File tree

linode_api4/polling.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,26 @@
66
from linode_api4.objects import Event
77

88

9+
class EventError(Exception):
10+
"""
11+
Represents a failed Linode event.
12+
"""
13+
14+
def __init__(self, event_id: int, message: Optional[str]):
15+
# Edge case, sometimes the message is populated with an empty string
16+
if len(message) < 1:
17+
message = None
18+
19+
self.event_id = event_id
20+
self.message = message
21+
22+
error_fmt = f"Event {event_id} failed"
23+
if message is not None:
24+
error_fmt += f": {message}"
25+
26+
super().__init__(error_fmt)
27+
28+
929
class TimeoutContext:
1030
"""
1131
TimeoutContext should be used by polling resources to track their provisioning time.
@@ -212,6 +232,10 @@ def wait_for_next_event_finished(
212232

213233
def poll_func():
214234
event._api_get()
235+
236+
if event.status == "failed":
237+
raise EventError(event.id, event.message)
238+
215239
return event.status in ["finished", "notification"]
216240

217241
if poll_func():

test/unit/objects/polling_test.py

Lines changed: 57 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
import json
2+
from typing import Optional
23

34
import httpretty
45
import pytest
56

67
from linode_api4 import LinodeClient
8+
from linode_api4.polling import EventError
79

810

911
class TestPolling:
@@ -12,7 +14,11 @@ def client(self):
1214
return LinodeClient("testing", base_url="https://localhost")
1315

1416
@staticmethod
15-
def body_event_status(status: str, action: str = "linode_shutdown"):
17+
def body_event_status(
18+
status: str,
19+
action: str = "linode_shutdown",
20+
message: Optional[str] = None,
21+
):
1622
return {
1723
"action": action,
1824
"entity": {
@@ -21,6 +27,7 @@ def body_event_status(status: str, action: str = "linode_shutdown"):
2127
},
2228
"id": 123,
2329
"status": status,
30+
"message": message,
2431
}
2532

2633
@staticmethod
@@ -272,3 +279,52 @@ def test_wait_for_event_finished_creation(
272279
assert len(get_requests) == 3
273280
assert result.entity.id == 11111
274281
assert result.status == "finished"
282+
283+
@httpretty.activate
284+
def test_wait_for_event_finished_failed(
285+
self,
286+
client,
287+
):
288+
"""
289+
Tests that the EventPoller.wait_for_event_finished method raises errors for failed events.
290+
"""
291+
292+
httpretty.register_uri(
293+
httpretty.GET,
294+
"https://localhost/account/events/123",
295+
responses=[
296+
httpretty.Response(
297+
body=json.dumps(self.body_event_status("started")),
298+
),
299+
httpretty.Response(
300+
body=json.dumps(
301+
self.body_event_status("failed", message="oh no!")
302+
),
303+
),
304+
],
305+
)
306+
307+
httpretty.register_uri(
308+
httpretty.GET,
309+
"https://localhost/account/events",
310+
responses=[
311+
httpretty.Response(
312+
body=json.dumps(self.body_event_list_empty()),
313+
status=200,
314+
),
315+
httpretty.Response(
316+
body=json.dumps(self.body_event_list_status("started")),
317+
status=200,
318+
),
319+
],
320+
)
321+
322+
try:
323+
client.polling.event_poller_create(
324+
"linode", "linode_shutdown", entity_id=11111
325+
).wait_for_next_event_finished(interval=0.1)
326+
except EventError as err:
327+
assert err.event_id == 123
328+
assert err.message == "oh no!"
329+
else:
330+
raise Exception("Expected event error, got none")

0 commit comments

Comments
 (0)