Merge pull request #19972 from gavindsouza/cli-scheduler-status
refactor(cli): Scheduler
This commit is contained in:
commit
c7e1bec0bd
4 changed files with 89 additions and 31 deletions
|
|
@ -376,6 +376,7 @@ def serve(
|
|||
"0.0.0.0",
|
||||
int(port),
|
||||
application,
|
||||
exclude_patterns=["test_*"],
|
||||
use_reloader=False if in_test_env else not no_reload,
|
||||
use_debugger=not in_test_env,
|
||||
use_evalex=not in_test_env,
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@ import click
|
|||
import frappe
|
||||
from frappe.commands import get_site, pass_context
|
||||
from frappe.exceptions import SiteNotSpecifiedError
|
||||
from frappe.utils import cint
|
||||
|
||||
|
||||
@click.command("trigger-scheduler-event", help="Trigger a scheduler event")
|
||||
|
|
@ -74,36 +73,40 @@ def disable_scheduler(context):
|
|||
|
||||
@click.command("scheduler")
|
||||
@click.option("--site", help="site name")
|
||||
@click.argument("state", type=click.Choice(["pause", "resume", "disable", "enable"]))
|
||||
@click.argument("state", type=click.Choice(["pause", "resume", "disable", "enable", "status"]))
|
||||
@click.option(
|
||||
"--format", "-f", default="text", type=click.Choice(["json", "text"]), help="Output format"
|
||||
)
|
||||
@click.option("--verbose", "-v", is_flag=True, help="Verbose output")
|
||||
@pass_context
|
||||
def scheduler(context, state, site=None):
|
||||
def scheduler(context, state: str, format: str, verbose: bool = False, site: str | None = None):
|
||||
"""Control scheduler state."""
|
||||
import frappe.utils.scheduler
|
||||
from frappe.installer import update_site_config
|
||||
import frappe
|
||||
from frappe.utils.scheduler import is_scheduler_inactive, toggle_scheduler
|
||||
|
||||
if not site:
|
||||
site = get_site(context)
|
||||
site = site or get_site(context)
|
||||
|
||||
try:
|
||||
frappe.init(site=site)
|
||||
output = {
|
||||
"text": "Scheduler is {status} for site {site}",
|
||||
"json": '{{"status": "{status}", "site": "{site}"}}',
|
||||
}
|
||||
|
||||
if state == "pause":
|
||||
update_site_config("pause_scheduler", 1)
|
||||
elif state == "resume":
|
||||
update_site_config("pause_scheduler", 0)
|
||||
elif state == "disable":
|
||||
frappe.connect()
|
||||
frappe.utils.scheduler.disable_scheduler()
|
||||
frappe.db.commit()
|
||||
elif state == "enable":
|
||||
frappe.connect()
|
||||
frappe.utils.scheduler.enable_scheduler()
|
||||
frappe.db.commit()
|
||||
with frappe.init_site(site=site):
|
||||
match state:
|
||||
case "status":
|
||||
frappe.connect()
|
||||
status = "disabled" if is_scheduler_inactive(verbose=verbose) else "enabled"
|
||||
return print(output[format].format(status=status, site=site))
|
||||
case "pause" | "resume":
|
||||
from frappe.installer import update_site_config
|
||||
|
||||
print(f"Scheduler {state}d for site {site}")
|
||||
update_site_config("pause_scheduler", state == "pause")
|
||||
case "enable" | "disable":
|
||||
frappe.connect()
|
||||
toggle_scheduler(state == "enable")
|
||||
frappe.db.commit()
|
||||
|
||||
finally:
|
||||
frappe.destroy()
|
||||
print(output[format].format(status=f"{state}d", site=site))
|
||||
|
||||
|
||||
@click.command("set-maintenance-mode")
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@ from frappe.tests.utils import FrappeTestCase, timeout
|
|||
from frappe.utils import add_to_date, get_bench_path, get_bench_relative_path, now
|
||||
from frappe.utils.backups import BackupGenerator, fetch_latest_backups
|
||||
from frappe.utils.jinja_globals import bundled_asset
|
||||
from frappe.utils.scheduler import enable_scheduler, is_scheduler_inactive
|
||||
|
||||
_result: Result | None = None
|
||||
TEST_SITE = "commands-site-O4PN2QKA.test" # added random string tag to avoid collisions
|
||||
|
|
@ -773,3 +774,52 @@ class TestDBCli(BaseTestCommands):
|
|||
def test_db_cli(self):
|
||||
self.execute("bench --site {site} db-console", kwargs={"cmd_input": rb"\q"})
|
||||
self.assertEqual(self.returncode, 0)
|
||||
|
||||
@run_only_if(db_type_is.MARIADB)
|
||||
def test_db_cli_with_sql(self):
|
||||
self.execute("bench --site {site} db-console -e 'select 1'")
|
||||
self.assertEqual(self.returncode, 0)
|
||||
self.assertIn("1", self.stdout)
|
||||
|
||||
|
||||
class TestSchedulerCLI(BaseTestCommands):
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super().setUpClass()
|
||||
cls.is_scheduler_active = not is_scheduler_inactive()
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
super().tearDownClass()
|
||||
if cls.is_scheduler_active:
|
||||
enable_scheduler()
|
||||
|
||||
def test_scheduler_status(self):
|
||||
self.execute("bench --site {site} scheduler status")
|
||||
self.assertEqual(self.returncode, 0)
|
||||
self.assertRegex(self.stdout, r"Scheduler is (disabled|enabled) for site .*")
|
||||
|
||||
self.execute("bench --site {site} scheduler status -f json")
|
||||
parsed_output = frappe.parse_json(self.stdout)
|
||||
self.assertEqual(self.returncode, 0)
|
||||
self.assertIsInstance(parsed_output, dict)
|
||||
self.assertIn("status", parsed_output)
|
||||
self.assertIn("site", parsed_output)
|
||||
|
||||
def test_scheduler_enable_disable(self):
|
||||
self.execute("bench --site {site} scheduler disable")
|
||||
self.assertEqual(self.returncode, 0)
|
||||
self.assertRegex(self.stdout, r"Scheduler is disabled for site .*")
|
||||
|
||||
self.execute("bench --site {site} scheduler enable")
|
||||
self.assertEqual(self.returncode, 0)
|
||||
self.assertRegex(self.stdout, r"Scheduler is enabled for site .*")
|
||||
|
||||
def test_scheduler_pause_resume(self):
|
||||
self.execute("bench --site {site} scheduler pause")
|
||||
self.assertEqual(self.returncode, 0)
|
||||
self.assertRegex(self.stdout, r"Scheduler is paused for site .*")
|
||||
|
||||
self.execute("bench --site {site} scheduler resume")
|
||||
self.assertEqual(self.returncode, 0)
|
||||
self.assertRegex(self.stdout, r"Scheduler is resumed for site .*")
|
||||
|
|
|
|||
|
|
@ -92,31 +92,35 @@ def enqueue_events(site: str) -> list[str] | None:
|
|||
return enqueued_jobs
|
||||
|
||||
|
||||
def is_scheduler_inactive() -> bool:
|
||||
def is_scheduler_inactive(verbose=True) -> bool:
|
||||
if frappe.local.conf.maintenance_mode:
|
||||
cprint(f"{frappe.local.site}: Maintenance mode is ON")
|
||||
if verbose:
|
||||
cprint(f"{frappe.local.site}: Maintenance mode is ON")
|
||||
return True
|
||||
|
||||
if frappe.local.conf.pause_scheduler:
|
||||
cprint(f"{frappe.local.site}: frappe.conf.pause_scheduler is SET")
|
||||
if verbose:
|
||||
cprint(f"{frappe.local.site}: frappe.conf.pause_scheduler is SET")
|
||||
return True
|
||||
|
||||
if is_scheduler_disabled():
|
||||
if is_scheduler_disabled(verbose=verbose):
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
|
||||
def is_scheduler_disabled() -> bool:
|
||||
def is_scheduler_disabled(verbose=True) -> bool:
|
||||
if frappe.conf.disable_scheduler:
|
||||
cprint(f"{frappe.local.site}: frappe.conf.disable_scheduler is SET")
|
||||
if verbose:
|
||||
cprint(f"{frappe.local.site}: frappe.conf.disable_scheduler is SET")
|
||||
return True
|
||||
|
||||
scheduler_disabled = not frappe.utils.cint(
|
||||
frappe.db.get_single_value("System Settings", "enable_scheduler")
|
||||
)
|
||||
if scheduler_disabled:
|
||||
cprint(f"{frappe.local.site}: SystemSettings.enable_scheduler is UNSET")
|
||||
if verbose:
|
||||
cprint(f"{frappe.local.site}: SystemSettings.enable_scheduler is UNSET")
|
||||
return scheduler_disabled
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue