def test_process_stop_cycle_point(get_point, options, expected):
    # Mock the get_point function - we don't care here
    get_point.return_value = None
    inputs = {
        'is_restart': False,
        'cfg': {
            'scheduling': {
                'stop after cycle point': '1993'
            }
        },
        'final_point': '2000',
    }
    inputs.update(options)

    # Create a mock scheduler object and assign values to it.
    scheduler = create_autospec(Scheduler)
    scheduler.is_restart = inputs.get('is_restart')
    scheduler.options = RunOptions(stopcp=inputs.get('cli_stop_point'))
    # Set-up fake config object
    scheduler.config = SimpleNamespace(final_point=inputs.get('final_point'),
                                       cfg=inputs.get('cfg'))
    # Set up fake taskpool
    scheduler.pool = Mock(stop_point=inputs.get('db_stop_point'))

    Scheduler.process_stop_cycle_point(scheduler)
    assert scheduler.options.stopcp == expected
Beispiel #2
0
async def _run_flow(
    caplog: Optional[pytest.LogCaptureFixture],
    schd: Scheduler,
    level: int = logging.INFO
):
    """Start a scheduler and set it running."""
    if caplog:
        caplog.set_level(level, CYLC_LOG)

    # install
    await schd.install()

    # start
    try:
        await schd.start()
    except Exception as exc:
        async with timeout(5):
            await schd.shutdown(exc)
    # run
    try:
        task = asyncio.create_task(schd.run_scheduler())
        yield caplog

    # stop
    finally:
        async with timeout(5):
            # ask the scheduler to shut down nicely
            schd._set_stop(StopMode.REQUEST_NOW_NOW)
            await task

        if task:
            # leave everything nice and tidy
            task.cancel()
Beispiel #3
0
async def _run_flow(run_dir: Path,
                    caplog: Optional[pytest.LogCaptureFixture],
                    scheduler: Scheduler,
                    level: int = logging.INFO):
    """Start a scheduler."""
    contact = (run_dir / scheduler.workflow / WorkflowFiles.Service.DIRNAME /
               WorkflowFiles.Service.CONTACT)
    if caplog:
        caplog.set_level(level, CYLC_LOG)
    task = None
    started = False
    await scheduler.install()
    try:
        task = asyncio.get_event_loop().create_task(scheduler.run())
        started = await _poll_file(contact)
        yield caplog
    finally:
        if started:
            # ask the scheduler to shut down nicely
            async with timeout(5):
                scheduler._set_stop(StopMode.REQUEST_NOW_NOW)
                await task

        if task:
            # leave everything nice and tidy
            task.cancel()
Beispiel #4
0
def test_check_startup_opts(opts: Dict[str, Optional[str]], is_restart: bool,
                            err: List[str]):
    """Test Scheduler.process_cycle_point_opts()"""
    mocked_scheduler = Mock()
    mocked_scheduler.options = Mock(spec=[], **opts)
    mocked_scheduler.is_restart = is_restart
    with pytest.raises(SchedulerError) as excinfo:
        Scheduler._check_startup_opts(mocked_scheduler)
    assert (err in str(excinfo))
Beispiel #5
0
def test_check_startup_opts(opts_to_test: List[str], is_restart: bool,
                            err_msg: str) -> None:
    """Test Scheduler._check_startup_opts()"""
    for opt in opts_to_test:
        mocked_scheduler = Mock(is_restart=is_restart)
        mocked_scheduler.options = SimpleNamespace(**{opt: 'reload'})
        with pytest.raises(UserInputError) as excinfo:
            Scheduler._check_startup_opts(mocked_scheduler)
        assert (err_msg.format(opt) in str(excinfo))
Beispiel #6
0
def scheduler_cli(parser, options, args, is_restart=False):
    """CLI main."""
    reg = args[0]
    # Check suite is not already running before start of host selection.
    try:
        suite_files.detect_old_contact_file(reg)
    except SuiteServiceFileError as exc:
        sys.exit(exc)

    suite_run_dir = get_suite_run_dir(reg)

    if not os.path.exists(suite_run_dir):
        sys.stderr.write(f'suite service directory not found '
                         f'at: {suite_run_dir}\n')
        sys.exit(1)

    # Create auth files if needed.
    suite_files.create_auth_files(reg)

    # Extract job.sh from library, for use in job scripts.
    extract_resources(suite_files.get_suite_srv_dir(reg), ['etc/job.sh'])

    # Check whether a run host is explicitly specified, else select one.
    if not options.host:
        try:
            host = HostAppointer().appoint_host()
        except EmptyHostList as exc:
            if cylc.flow.flags.debug:
                raise
            else:
                sys.exit(str(exc))
        if is_remote_host(host):
            if is_restart:
                base_cmd = ["restart"] + sys.argv[1:]
            else:
                base_cmd = ["run"] + sys.argv[1:]
            # Prevent recursive host selection
            base_cmd.append("--host=localhost")
            return remote_cylc_cmd(base_cmd, host=host)
    if remrun(set_rel_local=True):  # State localhost as above.
        sys.exit()

    try:
        suite_files.get_suite_source_dir(args[0], options.owner)
    except SuiteServiceFileError:
        # Source path is assumed to be the run directory
        suite_files.register(args[0], get_suite_run_dir(args[0]))

    try:
        scheduler = Scheduler(is_restart, options, args)
    except SuiteServiceFileError as exc:
        sys.exit(exc)
    scheduler.start()
Beispiel #7
0
def _make_scheduler(reg, **opts):
    """Return a scheduler object for a flow registration."""
    # This allows paused_start to be overriden:
    opts = {'paused_start': True, **opts}
    options = RunOptions(**opts)
    # create workflow
    return Scheduler(reg, options)
Beispiel #8
0
def test_process_cycle_point_opts(opts: Dict[str,
                                             Optional[str]], is_restart: bool,
                                  expected_set_opts: Dict[str, Optional[str]],
                                  expected_warnings: List[str],
                                  caplog: Fixture):
    """Test Scheduler.process_cycle_point_opts()"""
    caplog.set_level(logging.WARNING, CYLC_LOG)
    mocked_scheduler = Mock()
    mocked_scheduler.options = Mock(spec=[], **opts)
    mocked_scheduler.is_restart = is_restart

    Scheduler.process_cycle_point_opts(mocked_scheduler)
    for opt, value in expected_set_opts.items():
        assert getattr(mocked_scheduler.options, opt) == value
    actual_warnings: List[str] = [rec.message for rec in caplog.records]
    assert sorted(actual_warnings) == sorted(expected_warnings)
Beispiel #9
0
 def __make_scheduler(reg: str, **opts: Any) -> Scheduler:
     # This allows paused_start to be overriden:
     opts = {'paused_start': True, **opts}
     options = RunOptions(**opts)
     # create workflow
     nonlocal schd
     schd = Scheduler(reg, options)
     return schd
Beispiel #10
0
    def test_ioerror_is_ignored(self, mocked_get_suite_source_dir):
        """Test that IOError's are ignored when closing Scheduler logs.
        When a disk errors occurs, the scheduler.close_logs method may
        result in an IOError. This, combined with other variables, may cause
        an infinite loop. So it is better that it is ignored."""
        mocked_get_suite_source_dir.return_value = '.'
        options = Options()
        args = ["suiteA"]
        scheduler = Scheduler(is_restart=False, options=options, args=args)

        handler = mock.MagicMock()
        handler.close.side_effect = IOError
        handler.level = logging.INFO
        LOG.addHandler(handler)

        scheduler.close_logs()
        self.assertEqual(1, handler.close.call_count)
        LOG.removeHandler(handler)
Beispiel #11
0
def _make_scheduler(reg, is_restart=False, **opts):
    """Return a scheduler object for a flow registration."""
    opts = {'hold_start': True, **opts}
    # get options object
    if is_restart:
        options = RestartOptions(**opts)
    else:
        options = RunOptions(**opts)
    # create workflow
    return Scheduler(reg, options, is_restart=is_restart)
Beispiel #12
0
def scheduler_cli(parser, options, args, is_restart=False):
    """CLI main."""
    # Check suite is not already running before start of host selection.
    try:
        SuiteSrvFilesManager().detect_old_contact_file(args[0])
    except SuiteServiceFileError as exc:
        sys.exit(exc)

    # Create auth files if needed.
    SuiteSrvFilesManager().create_auth_files(args[0])

    # Check whether a run host is explicitly specified, else select one.
    if not options.host:
        try:
            host = HostAppointer().appoint_host()
        except EmptyHostList as exc:
            if cylc.flow.flags.debug:
                raise
            else:
                sys.exit(str(exc))
        if is_remote_host(host):
            if is_restart:
                base_cmd = ["restart"] + sys.argv[1:]
            else:
                base_cmd = ["run"] + sys.argv[1:]
            # Prevent recursive host selection
            base_cmd.append("--host=localhost")
            return remote_cylc_cmd(base_cmd, host=host)
    if remrun(set_rel_local=True):  # State localhost as above.
        sys.exit()

    try:
        SuiteSrvFilesManager().get_suite_source_dir(args[0], options.owner)
    except SuiteServiceFileError:
        # Source path is assumed to be the run directory
        SuiteSrvFilesManager().register(args[0], get_suite_run_dir(args[0]))

    try:
        scheduler = Scheduler(is_restart, options, args)
    except SuiteServiceFileError as exc:
        sys.exit(exc)
    scheduler.start()
Beispiel #13
0
    def test_ioerror_is_ignored(self, mocked_suite_srv_files_mgr,
                                mocked_suite_db_mgr, mocked_broadcast_mgr):
        """Test that IOError's are ignored when closing Scheduler logs.
        When a disk errors occurs, the scheduler.close_logs method may
        result in an IOError. This, combined with other variables, may cause
        an infinite loop. So it is better that it is ignored."""
        mocked_suite_srv_files_mgr.return_value\
            .get_suite_source_dir.return_value = "."
        options = Options()
        args = ["suiteA"]
        scheduler = Scheduler(is_restart=False, options=options, args=args)

        handler = mock.MagicMock()
        handler.close.side_effect = IOError
        handler.level = logging.INFO
        LOG.addHandler(handler)

        scheduler.close_logs()
        self.assertEqual(1, handler.close.call_count)
        LOG.removeHandler(handler)
Beispiel #14
0
def scheduler_cli(parser, options, args, is_restart=False):
    """Implement cylc (run|restart).

    This function should contain all of the command line facing
    functionality of the Scheduler, exit codes, logging, etc.

    The Scheduler itself should be a Python object you can import and
    run in a regular Python session so cannot contain this kind of
    functionality.

    """
    reg = args[0]
    # Check suite is not already running before start of host selection.
    try:
        suite_files.detect_old_contact_file(reg)
    except SuiteServiceFileError as exc:
        sys.exit(exc)

    _check_registration(reg)

    # re-execute on another host if required
    _distribute(options.host, is_restart)

    # print the start message
    if options.no_detach or options.format == 'plain':
        _start_print_blurb()

    # setup the scheduler
    # NOTE: asyncio.run opens an event loop, runs your coro,
    #       then shutdown async generators and closes the event loop
    scheduler = Scheduler(reg, options, is_restart=is_restart)
    asyncio.run(_setup(parser, options, reg, is_restart, scheduler))

    # daemonize if requested
    # NOTE: asyncio event loops cannot persist across daemonization
    #       ensure you have tidied up all threads etc before daemonizing
    if not options.no_detach:
        from cylc.flow.daemonize import daemonize
        daemonize(scheduler)

    # setup loggers
    _open_logs(reg, options.no_detach)

    # run the workflow
    ret = asyncio.run(_run(parser, options, reg, is_restart, scheduler))

    # exit
    # NOTE: we must clean up all asyncio / threading stuff before exiting
    # NOTE: any threads which include sleep statements could cause
    #       sys.exit to hang if not shutdown properly
    LOG.info("DONE")
    _close_logs()
    sys.exit(ret)
Beispiel #15
0
async def _run_flow(caplog: Optional[pytest.LogCaptureFixture],
                    schd: Scheduler,
                    level: int = logging.INFO):
    """Start a scheduler and set the main loop running."""
    if caplog:
        caplog.set_level(level, CYLC_LOG)

    await schd.install()

    task: Optional[asyncio.Task] = None
    try:
        # Nested `try...finally` to ensure caplog always yielded even if
        # exception occurs in Scheduler
        try:
            await schd.start()
            # Do not await as we need to yield control to the main loop:
            task = asyncio.create_task(schd.run_scheduler())
        finally:
            # After this `yield`, the `with` block of the context manager
            # is executed:
            yield caplog
    finally:
        # Cleanup - this always runs after the `with` block of the
        # context manager.
        # Need to shut down Scheduler, but time out in case something
        # goes wrong:
        async with timeout(5):
            if task:
                # ask the scheduler to shut down nicely,
                # let main loop handle it:
                schd._set_stop(StopMode.REQUEST_NOW_NOW)
                await task
        if schd.contact_data:
            async with timeout(5):
                # Scheduler still running... try more forceful tear down:
                await schd.shutdown(SchedulerStop("integration test teardown"))
        if task:
            # Brute force cleanup if something went wrong:
            task.cancel()
Beispiel #16
0
def scheduler_cli(options: 'Values', workflow_id: str) -> None:
    """Run the workflow.

    This function should contain all of the command line facing
    functionality of the Scheduler, exit codes, logging, etc.

    The Scheduler itself should be a Python object you can import and
    run in a regular Python session so cannot contain this kind of
    functionality.

    """
    # Parse workflow name but delay Cylc 7 suiter.rc deprecation warning
    # until after the start-up splash is printed.
    # TODO: singleton
    (workflow_id, ), _ = parse_ids(
        workflow_id,
        constraint='workflows',
        max_workflows=1,
        # warn_depr=False,  # TODO
    )
    try:
        detect_old_contact_file(workflow_id)
    except ServiceFileError as exc:
        print(f"Resuming already-running workflow\n\n{exc}")
        pclient = WorkflowRuntimeClient(
            workflow_id,
            timeout=options.comms_timeout,
        )
        mutation_kwargs = {
            'request_string': RESUME_MUTATION,
            'variables': {
                'wFlows': [workflow_id]
            }
        }
        pclient('graphql', mutation_kwargs)
        sys.exit(0)

    # re-execute on another host if required
    _distribute(options.host)

    # print the start message
    if (cylc.flow.flags.verbosity > -1
            and (options.no_detach or options.format == 'plain')):
        print(cparse(cylc_header()))

    if cylc.flow.flags.cylc7_back_compat:
        LOG.warning(SUITERC_DEPR_MSG)

    # setup the scheduler
    # NOTE: asyncio.run opens an event loop, runs your coro,
    #       then shutdown async generators and closes the event loop
    scheduler = Scheduler(workflow_id, options)
    asyncio.run(_setup(scheduler))

    # daemonize if requested
    # NOTE: asyncio event loops cannot persist across daemonization
    #       ensure you have tidied up all threads etc before daemonizing
    if not options.no_detach:
        from cylc.flow.daemonize import daemonize
        daemonize(scheduler)

    # setup loggers
    _open_logs(workflow_id, options.no_detach)

    # run the workflow
    ret = asyncio.run(_run(scheduler))

    # exit
    # NOTE: we must clean up all asyncio / threading stuff before exiting
    # NOTE: any threads which include sleep statements could cause
    #       sys.exit to hang if not shutdown properly
    LOG.info("DONE")
    close_log(LOG)
    sys.exit(ret)
Beispiel #17
0
def scheduler_cli(parser, options, reg):
    """Run the workflow.

    This function should contain all of the command line facing
    functionality of the Scheduler, exit codes, logging, etc.

    The Scheduler itself should be a Python object you can import and
    run in a regular Python session so cannot contain this kind of
    functionality.

    """
    workflow_files.validate_flow_name(reg)
    reg = os.path.normpath(reg)
    try:
        workflow_files.detect_old_contact_file(reg)
    except ServiceFileError as exc:
        print(f"Resuming already-running workflow\n\n{exc}")
        pclient = WorkflowRuntimeClient(reg, timeout=options.comms_timeout)
        mutation_kwargs = {
            'request_string': RESUME_MUTATION,
            'variables': {
                'wFlows': [reg]
            }
        }
        pclient('graphql', mutation_kwargs)
        sys.exit(0)

    # re-execute on another host if required
    _distribute(options.host)

    # print the start message
    if (cylc.flow.flags.verbosity > -1
            and (options.no_detach or options.format == 'plain')):
        print(cparse(cylc_header()))

    # setup the scheduler
    # NOTE: asyncio.run opens an event loop, runs your coro,
    #       then shutdown async generators and closes the event loop
    scheduler = Scheduler(reg, options)
    asyncio.run(_setup(scheduler))

    # daemonize if requested
    # NOTE: asyncio event loops cannot persist across daemonization
    #       ensure you have tidied up all threads etc before daemonizing
    if not options.no_detach:
        from cylc.flow.daemonize import daemonize
        daemonize(scheduler)

    # setup loggers
    _open_logs(reg, options.no_detach)

    # run the workflow
    ret = asyncio.run(_run(scheduler))

    # exit
    # NOTE: we must clean up all asyncio / threading stuff before exiting
    # NOTE: any threads which include sleep statements could cause
    #       sys.exit to hang if not shutdown properly
    LOG.info("DONE")
    _close_logs()
    sys.exit(ret)