-
-
Notifications
You must be signed in to change notification settings - Fork 4.6k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Make custom remote control commands available in CLI (#8489)
* Make custom remote control commands available in CLI * fixup (remove accidentally commited todo comments) * Avoid breaking test_worker by modifying os.environ * Reset global state after each preload test
- Loading branch information
Showing
4 changed files
with
206 additions
and
58 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
from celery import Celery | ||
from celery.worker.control import control_command, inspect_command | ||
|
||
|
||
@control_command( | ||
args=[('a', int), ('b', int)], | ||
signature='a b', | ||
) | ||
def custom_control_cmd(state, a, b): | ||
"""Ask the workers to reply with a and b.""" | ||
return {'ok': f'Received {a} and {b}'} | ||
|
||
|
||
@inspect_command( | ||
args=[('x', int)], | ||
signature='x', | ||
) | ||
def custom_inspect_cmd(state, x): | ||
"""Ask the workers to reply with x.""" | ||
return {'ok': f'Received {x}'} | ||
|
||
|
||
app = Celery(set_as_current=False) | ||
app.config_from_object('t.integration.test_worker_config') |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
import os | ||
import re | ||
from unittest.mock import patch | ||
|
||
import pytest | ||
from click.testing import CliRunner | ||
|
||
from celery.bin.celery import celery | ||
from celery.platforms import EX_UNAVAILABLE | ||
|
||
_GLOBAL_OPTIONS = ['-A', 't.unit.bin.proj.app_with_custom_cmds', '--broker', 'memory://'] | ||
_INSPECT_OPTIONS = ['--timeout', '0'] # Avoid waiting for the zero workers to reply | ||
|
||
|
||
@pytest.fixture(autouse=True) | ||
def clean_os_environ(): | ||
# Celery modifies os.environ when given the CLI option --broker memory:// | ||
# This interferes with other tests, so we need to reset os.environ | ||
with patch.dict(os.environ, clear=True): | ||
yield | ||
|
||
|
||
@pytest.mark.parametrize( | ||
('celery_cmd', 'custom_cmd'), | ||
[ | ||
('inspect', ('custom_inspect_cmd', '123')), | ||
('control', ('custom_control_cmd', '123', '456')), | ||
], | ||
) | ||
def test_custom_remote_command(celery_cmd, custom_cmd, isolated_cli_runner: CliRunner): | ||
res = isolated_cli_runner.invoke( | ||
celery, | ||
[*_GLOBAL_OPTIONS, celery_cmd, *_INSPECT_OPTIONS, *custom_cmd], | ||
catch_exceptions=False, | ||
) | ||
assert res.exit_code == EX_UNAVAILABLE, (res, res.stdout) | ||
assert res.stdout.strip() == 'Error: No nodes replied within time constraint' | ||
|
||
|
||
@pytest.mark.parametrize( | ||
('celery_cmd', 'remote_cmd'), | ||
[ | ||
# Test nonexistent commands | ||
('inspect', 'this_command_does_not_exist'), | ||
('control', 'this_command_does_not_exist'), | ||
# Test commands that exist, but are of the wrong type | ||
('inspect', 'custom_control_cmd'), | ||
('control', 'custom_inspect_cmd'), | ||
], | ||
) | ||
def test_unrecognized_remote_command(celery_cmd, remote_cmd, isolated_cli_runner: CliRunner): | ||
res = isolated_cli_runner.invoke( | ||
celery, | ||
[*_GLOBAL_OPTIONS, celery_cmd, *_INSPECT_OPTIONS, remote_cmd], | ||
catch_exceptions=False, | ||
) | ||
assert res.exit_code == 2, (res, res.stdout) | ||
assert f'Error: Command {remote_cmd} not recognized. Available {celery_cmd} commands: ' in res.stdout | ||
|
||
|
||
_expected_inspect_regex = ( | ||
'\n custom_inspect_cmd x\\s+Ask the workers to reply with x\\.\n' | ||
) | ||
_expected_control_regex = ( | ||
'\n custom_control_cmd a b\\s+Ask the workers to reply with a and b\\.\n' | ||
) | ||
|
||
|
||
@pytest.mark.parametrize( | ||
('celery_cmd', 'expected_regex'), | ||
[ | ||
('inspect', re.compile(_expected_inspect_regex, re.MULTILINE)), | ||
('control', re.compile(_expected_control_regex, re.MULTILINE)), | ||
], | ||
) | ||
def test_listing_remote_commands(celery_cmd, expected_regex, isolated_cli_runner: CliRunner): | ||
res = isolated_cli_runner.invoke( | ||
celery, | ||
[*_GLOBAL_OPTIONS, celery_cmd, '--list'], | ||
) | ||
assert res.exit_code == 0, (res, res.stdout) | ||
assert expected_regex.search(res.stdout) |