Skip to content

Commit 28ee491

Browse files
committed
fix: fail fast on Cinema 4D license failures
Signed-off-by: Karthik Bekal Pattathana <133984042+karthikbekalp@users.noreply.github.com>
1 parent 0364064 commit 28ee491

2 files changed

Lines changed: 84 additions & 0 deletions

File tree

  • src/deadline/cinema4d_adaptor/Cinema4DAdaptor
  • test/unit/deadline_adaptor_for_cinema4d/Cinema4DAdaptor

src/deadline/cinema4d_adaptor/Cinema4DAdaptor/adaptor.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -314,6 +314,18 @@ def _get_regex_callbacks(self) -> list[RegexCallback]:
314314
)
315315
)
316316

317+
license_error_regexes = [
318+
re.compile(r".*IsFloating query failed.*", re.IGNORECASE),
319+
re.compile(r".*No license found\..*\(2030\).*", re.IGNORECASE),
320+
re.compile(r".*No available licenses to choose\..*", re.IGNORECASE),
321+
]
322+
callback_list.append(
323+
RegexCallback(
324+
license_error_regexes,
325+
self._handle_license_error,
326+
)
327+
)
328+
317329
self._regex_callbacks = callback_list
318330
return self._regex_callbacks
319331

@@ -404,6 +416,19 @@ def _handle_nvidia_driver_error(self, match: re.Match) -> None:
404416
)
405417
self._exc_info = RuntimeError(message)
406418

419+
def _handle_license_error(self, match: re.Match) -> None:
420+
"""Handle a fatal Cinema 4D licensing failure."""
421+
message = (
422+
"Cinema 4D failed to acquire a license.\n"
423+
"If you are using bring your own license (BYOL), check your license configuration "
424+
"and availability.\n"
425+
"If you are using usage-based licensing (UBL) from AWS Deadline Cloud and need a "
426+
"higher 'License sessions per license endpoint' limit, contact the AWS Deadline Cloud "
427+
"team to request an increase.\n"
428+
f"Error: {match.group(0)}"
429+
)
430+
self._exc_info = RuntimeError(message)
431+
407432
def _add_deadline_openjd_paths(self) -> None:
408433
# Add the openjd namespace directory to PYTHONPATH, so that adaptor_runtime_client
409434
# will be available directly to the adaptor client.

test/unit/deadline_adaptor_for_cinema4d/Cinema4DAdaptor/test_adaptor.py

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
# the same Cinema4DAdaptor action queue, which can cause race conditions and
77
# test failures when tests run in parallel across multiple workers.
88
import json
9+
import logging
910
from pathlib import Path
1011
from unittest.mock import Mock, PropertyMock, patch
1112

@@ -364,6 +365,64 @@ def test__wait_for_socket(
364365
# THEN
365366
assert mock_sleep.call_count == 3
366367

368+
@pytest.mark.parametrize(
369+
"license_error",
370+
[
371+
"17:44:34 Error running authentication: IsFloating query failed.",
372+
"17:44:34 No license found. [.(2030)]",
373+
"18:38:54 No available licenses to choose.",
374+
],
375+
)
376+
def test_license_failure_from_stdout_interrupts_startup(
377+
self, init_data: dict, license_error: str
378+
) -> None:
379+
"""Tests that a licensing prompt fails startup without waiting for the timeout."""
380+
# General error checking is optional, but a licensing prompt cannot recover
381+
# without interactive input and must always stop the worker.
382+
init_data["activate_error_checking"] = "0"
383+
adaptor = Cinema4DAdaptor(init_data)
384+
expected_error = (
385+
"Cinema 4D failed to acquire a license.\n"
386+
"If you are using bring your own license (BYOL), check your license configuration "
387+
"and availability.\n"
388+
"If you are using usage-based licensing (UBL) from AWS Deadline Cloud and need a "
389+
"higher 'License sessions per license endpoint' limit, contact the AWS Deadline Cloud "
390+
"team to request an increase.\n"
391+
f"Error: {license_error}"
392+
)
393+
394+
def emit_license_error(*args, **kwargs):
395+
kwargs["stdout_handler"].emit(
396+
logging.LogRecord(
397+
name="cinema4d",
398+
level=logging.ERROR,
399+
pathname="",
400+
lineno=0,
401+
msg=license_error,
402+
args=(),
403+
exc_info=None,
404+
)
405+
)
406+
process = Mock()
407+
process.is_running = True
408+
return process
409+
410+
with (
411+
patch.object(adaptor, "_initialize_maxon_assets_db_connection"),
412+
patch.object(adaptor, "_start_cinema4d_server_thread"),
413+
patch.object(adaptor, "_populate_action_queue"),
414+
patch(
415+
"deadline.cinema4d_adaptor.Cinema4DAdaptor.adaptor.LoggingSubprocess",
416+
side_effect=emit_license_error,
417+
),
418+
patch("time.sleep") as mock_sleep,
419+
pytest.raises(RuntimeError) as exc_info,
420+
):
421+
adaptor.on_start()
422+
423+
assert str(exc_info.value) == expected_error
424+
mock_sleep.assert_not_called()
425+
367426

368427
@pytest.mark.xdist_group(name="adaptor_tests")
369428
class TestCinema4DAdaptor_on_run:

0 commit comments

Comments
 (0)