diff --git a/changes.d/6509.feat.md b/changes.d/6509.feat.md new file mode 100644 index 0000000000..8406379562 --- /dev/null +++ b/changes.d/6509.feat.md @@ -0,0 +1 @@ +Added --global flag to 'cylc reload' diff --git a/cylc/flow/cfgspec/globalcfg.py b/cylc/flow/cfgspec/globalcfg.py index 18570a65c3..06b7265ec7 100644 --- a/cylc/flow/cfgspec/globalcfg.py +++ b/cylc/flow/cfgspec/globalcfg.py @@ -108,6 +108,13 @@ Not to be confused with :cylc:conf:`flow.cylc[scheduling]`. +.. note:: + + The majority of scheduler settings affect the server and cannot be reloaded + with ``cylc reload --global``, the server must be stopped and restarted for + changes to take effect except for the sections [mail] and [events] which + provide workflow defaults. + .. versionchanged:: 8.0.0 {REPLACES}``[cylc]`` @@ -1171,6 +1178,11 @@ def default_for( Symlinks from the the standard ``$HOME/cylc-run`` locations will be created. + .. note:: + + Once a platform has been installed and symlinks created they + cannot be modified for that run. + .. versionadded:: 8.0.0 """): with Conf('', desc=dedent(""" diff --git a/cylc/flow/commands.py b/cylc/flow/commands.py index c45194752a..823022f52d 100644 --- a/cylc/flow/commands.py +++ b/cylc/flow/commands.py @@ -85,6 +85,7 @@ from cylc.flow.parsec.exceptions import ParsecError from cylc.flow.run_modes import RunMode from cylc.flow.task_id import TaskID +from cylc.flow.cfgspec.glbl_cfg import glbl_cfg from cylc.flow.workflow_status import StopMode @@ -327,7 +328,7 @@ async def remove_tasks( @_command('reload_workflow') -async def reload_workflow(schd: 'Scheduler'): +async def reload_workflow(schd: 'Scheduler', reload_global: bool = False): """Reload workflow configuration.""" yield # pause the workflow if not already @@ -361,6 +362,25 @@ async def reload_workflow(schd: 'Scheduler'): # give commands time to complete sleep(1) # give any remove-init's time to complete + if reload_global: + # Reload global config if requested + LOG.info("Reloading the global configuration.") + try: + glbl_cfg(reload=True) + except (ParsecError, CylcConfigError) as exc: + if cylc.flow.flags.verbosity > 1: + # log full traceback in debug mode + LOG.exception(exc) + LOG.critical( + f'Reload failed - {exc.__class__.__name__}: {exc}' + '\nThis is probably due to an issue with the new' + ' configuration.' + '\nTo continue with the pre-reload config, un-pause the' + ' workflow.' + '\nOtherwise, fix the configuration and attempt to reload' + ' again.' + ) + # reload the workflow definition schd.reload_pending = 'loading the workflow definition' schd.update_data_store() # update workflow status msg diff --git a/cylc/flow/network/schema.py b/cylc/flow/network/schema.py index 3f6534b418..5f2d6c7598 100644 --- a/cylc/flow/network/schema.py +++ b/cylc/flow/network/schema.py @@ -1919,6 +1919,10 @@ class Meta: class Arguments: workflows = graphene.List(WorkflowID, required=True) + reload_global = Boolean( + default_value=False, + description="Also reload global config") + result = GenericScalar() diff --git a/cylc/flow/scripts/reload.py b/cylc/flow/scripts/reload.py index ca11062e9b..c8e39989d1 100755 --- a/cylc/flow/scripts/reload.py +++ b/cylc/flow/scripts/reload.py @@ -60,6 +60,7 @@ from cylc.flow.option_parsers import ( WORKFLOW_ID_MULTI_ARG_DOC, CylcOptionParser as COP, + OptionSettings, ) from cylc.flow.terminal import cli_function @@ -67,12 +68,26 @@ from optparse import Values +RELOAD_OPTIONS = [ + OptionSettings( + ['-g', '--global'], + help='also reload global configuration.', + action="store_true", + default=False, + dest="reload_global", + sources={'reload'} + ), +] + + MUTATION = ''' mutation ( - $wFlows: [WorkflowID]! + $wFlows: [WorkflowID]!, + $reloadGlobal: Boolean, ) { reload ( workflows: $wFlows + reloadGlobal: $reloadGlobal ) { result } @@ -87,6 +102,9 @@ def get_option_parser(): multiworkflow=True, argdoc=[WORKFLOW_ID_MULTI_ARG_DOC], ) + for option in RELOAD_OPTIONS: + parser.add_option(*option.args, **option.kwargs) + return parser @@ -97,6 +115,7 @@ async def run(options: 'Values', workflow_id: str): 'request_string': MUTATION, 'variables': { 'wFlows': [workflow_id], + 'reloadGlobal': options.reload_global, } } diff --git a/cylc/flow/scripts/validate_reinstall.py b/cylc/flow/scripts/validate_reinstall.py index 4e78d673d7..afb36ddeab 100644 --- a/cylc/flow/scripts/validate_reinstall.py +++ b/cylc/flow/scripts/validate_reinstall.py @@ -73,6 +73,7 @@ reinstall_cli as cylc_reinstall, ) from cylc.flow.scripts.reload import ( + RELOAD_OPTIONS, run as cylc_reload ) from cylc.flow.terminal import cli_function @@ -86,6 +87,7 @@ VALIDATE_OPTIONS, REINSTALL_OPTIONS, REINSTALL_CYLC_ROSE_OPTIONS, + RELOAD_OPTIONS, PLAY_OPTIONS, CYLC_ROSE_OPTIONS, modify={'cylc-rose': 'validate, install'} @@ -102,6 +104,7 @@ def get_option_parser() -> COP: for option in VR_OPTIONS: parser.add_option(*option.args, **option.kwargs) parser.set_defaults(is_validate=True) + return parser diff --git a/tests/functional/reload/29-global.t b/tests/functional/reload/29-global.t new file mode 100644 index 0000000000..2ffa716b0b --- /dev/null +++ b/tests/functional/reload/29-global.t @@ -0,0 +1,47 @@ +#!/usr/bin/env bash +# THIS FILE IS PART OF THE CYLC WORKFLOW ENGINE. +# Copyright (C) NIWA & British Crown (Met Office) & Contributors. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +#------------------------------------------------------------------------------- +# Test reloading global configuration +. "$(dirname "$0")/test_header" +set_test_number 9 + +create_test_global_config "" "" + +TEST_NAME="${TEST_NAME_BASE}" +install_workflow "${TEST_NAME}" "${TEST_NAME_BASE}" + +# Validate the config +run_ok "${TEST_NAME}-validate" cylc validate "${WORKFLOW_NAME}" + +# Run the workflow +workflow_run_ok "${TEST_NAME_BASE}-run" cylc play "${WORKFLOW_NAME}" --no-detach -v + +# Reload happened +grep_ok "Reloading the global configuration" "${WORKFLOW_RUN_DIR}/log/scheduler/01-start-01.log" + +# Reload hasn't happened in job a, has happened in b and c +grep_fail "global init-script reloaded!" "${WORKFLOW_RUN_DIR}/log/job/1/a/01/job.out" +grep_ok "global init-script reloaded!" "${WORKFLOW_RUN_DIR}/log/job/1/b/01/job.out" +grep_ok "global init-script reloaded!" "${WORKFLOW_RUN_DIR}/log/job/1/c/01/job.out" + +# Events are original in job a, updated in b and c +grep_fail "!!EVENT!! succeeded 1/a" "${WORKFLOW_RUN_DIR}/log/scheduler/01-start-01.log" +grep_ok "!!EVENT!! succeeded 1/b" "${WORKFLOW_RUN_DIR}/log/scheduler/01-start-01.log" +grep_ok "!!EVENT!! succeeded 1/c" "${WORKFLOW_RUN_DIR}/log/scheduler/01-start-01.log" + +purge +exit diff --git a/tests/functional/reload/29-global/flow.cylc b/tests/functional/reload/29-global/flow.cylc new file mode 100644 index 0000000000..e1584c977a --- /dev/null +++ b/tests/functional/reload/29-global/flow.cylc @@ -0,0 +1,30 @@ +[scheduling] + cycling mode = integer + [[graph]] + R1 = a => reload-global => b & c +[runtime] + [[root]] + script = true + [[a,b]] + platform = localhost + [[c]] + platform = $CYLC_TEST_PLATFORM + [[reload-global]] + platform = localhost + script = """ + # Append to global config + cat >> "$CYLC_CONF_PATH/global.cylc" <