Skip to content

Commit ed174a7

Browse files
committed
feat: Add support to fetch and match worker logs
1 parent 3e85e5a commit ed174a7

File tree

3 files changed

+67
-4
lines changed

3 files changed

+67
-4
lines changed

src/deadline_test_fixtures/deadline/__init__.py

+2
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
Task,
2424
TaskParameterValue,
2525
TaskStatus,
26+
WorkerLog,
2627
)
2728
from .worker import (
2829
CommandResult,
@@ -70,4 +71,5 @@
7071
"TaskStatus",
7172
"WindowsInstanceBuildWorker",
7273
"WindowsInstanceWorkerBase",
74+
"WorkerLog",
7375
]

src/deadline_test_fixtures/deadline/resources.py

+11-2
Original file line numberDiff line numberDiff line change
@@ -1288,8 +1288,7 @@ def assert_log_does_not_contain(
12881288

12891289

12901290
@dataclass
1291-
class SessionLog:
1292-
session_id: str
1291+
class CloudWatchLogs:
12931292
logs: list[CloudWatchLogEvent]
12941293

12951294
def assert_pattern_in_log(
@@ -1365,3 +1364,13 @@ def from_api_response(response: dict) -> CloudWatchLogEvent:
13651364
message=response["message"],
13661365
timestamp=response["timestamp"],
13671366
)
1367+
1368+
1369+
@dataclass
1370+
class SessionLog(CloudWatchLogs):
1371+
session_id: str
1372+
1373+
1374+
@dataclass
1375+
class WorkerLog(CloudWatchLogs):
1376+
worker_id: str

src/deadline_test_fixtures/deadline/worker.py

+54-2
Original file line numberDiff line numberDiff line change
@@ -16,15 +16,18 @@
1616
import tempfile
1717
import time
1818
from dataclasses import dataclass, field, InitVar, replace
19-
from typing import Any, ClassVar, Optional, cast
19+
from typing import TYPE_CHECKING, Any, ClassVar, Optional, cast
2020

2121
from ..models import (
2222
PipInstall,
2323
PosixSessionUser,
2424
)
25-
from .resources import Fleet
25+
from .resources import CloudWatchLogEvent, Fleet, WorkerLog
2626
from ..util import call_api, wait_for
2727

28+
if TYPE_CHECKING:
29+
from botocore.paginate import PageIterator, Paginator
30+
2831
LOG = logging.getLogger(__name__)
2932

3033
DOCKER_CONTEXT_DIR = os.path.join(os.path.dirname(__file__), "..", "containers", "worker")
@@ -48,6 +51,15 @@ def get_worker_id(self) -> str:
4851
pass
4952

5053

54+
@dataclass(frozen=True)
55+
class WorkerLogConfig:
56+
cloudwatch_log_group: str
57+
"""The name of the CloudWatch Log Group that the Agent log should be streamed to"""
58+
59+
cloudwatch_log_stream: str
60+
"""The name of the CloudWatch Log Stream that the Agent log should be streamed to"""
61+
62+
5163
@dataclass(frozen=True)
5264
class CommandResult: # pragma: no cover
5365
exit_code: int
@@ -252,6 +264,46 @@ def set_stopped_status(self):
252264
LOG.exception(f"Failed to update worker status: {error}")
253265
raise
254266

267+
def get_worker_logs(self) -> Optional[WorkerLogConfig]:
268+
"""Get the log group and log stream for the worker. Retain the API structure"""
269+
response = self.deadline_client.get_worker(
270+
farmId=self.configuration.farm_id,
271+
fleetId=self.configuration.fleet.id,
272+
workerId=self.worker_id,
273+
)
274+
if log_config := response["log"]:
275+
LOG.info(f"LogGroup structure {log_config}")
276+
if log_config_options := log_config.get("options"):
277+
log_group_name = log_config_options.get("logGroupName")
278+
log_stream_name = log_config_options.get("logStreamName")
279+
if log_group_name and log_stream_name:
280+
return WorkerLogConfig(
281+
cloudwatch_log_group=log_group_name, cloudwatch_log_stream=log_stream_name
282+
)
283+
# Default, no log group yet.
284+
return None
285+
286+
def get_all_worker_logs(self, *, logs_client: botocore.client.BaseClient) -> WorkerLog:
287+
# Get the worker log group and stream from the service.
288+
log_config = self.get_worker_logs()
289+
if not log_config:
290+
return WorkerLog(worker_id=self.worker_id, logs=[]) # type: ignore[arg-type]
291+
292+
filter_log_events_paginator: Paginator = logs_client.get_paginator("filter_log_events")
293+
filter_log_events_pages: PageIterator = call_api(
294+
description=f"Fetching log events for session {self.worker_id} in log group {log_config.cloudwatch_log_group}",
295+
fn=lambda: filter_log_events_paginator.paginate(
296+
logGroupName=log_config.cloudwatch_log_group,
297+
logStreamNames=[log_config.cloudwatch_log_stream],
298+
),
299+
)
300+
log_events = filter_log_events_pages.build_full_result()
301+
log_events = [CloudWatchLogEvent.from_api_response(e) for e in log_events["events"]]
302+
# For debugging test cases.
303+
# LOG.info(log_events)
304+
305+
return WorkerLog(worker_id=self.worker_id, logs=log_events) # type: ignore[arg-type]
306+
255307
def send_command(self, command: str) -> CommandResult:
256308
"""Send a command via SSM to a shell on a launched EC2 instance. Once the command has fully
257309
finished the result of the invocation is returned.

0 commit comments

Comments
 (0)