mirror of
				https://github.com/arcan1s/ahriman.git
				synced 2025-11-04 07:43:42 +00:00 
			
		
		
		
	Remote call trigger support (#105)
* add support of remote task tracking * add remote call trigger implementation * docs update * add cross-service upload * add notes about user * add more ability to control upload * multipart upload with signatures as well as safe file save * configuration reference update * rename watcher methods * erase logs based on current package version Old implementation has used process id instead, but it leads to log removal in case of remote process trigger * add --server flag for setup command * restore behavior of the httploghandler
This commit is contained in:
		@ -39,7 +39,7 @@ def args() -> argparse.Namespace:
 | 
			
		||||
    Returns:
 | 
			
		||||
        argparse.Namespace: command line arguments test instance
 | 
			
		||||
    """
 | 
			
		||||
    return argparse.Namespace(architecture=None, lock=None, force=False, unsafe=False, report=False)
 | 
			
		||||
    return argparse.Namespace(architecture=None, lock=None, force=False, unsafe=False, report=False, wait_timeout=-1)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@pytest.fixture
 | 
			
		||||
 | 
			
		||||
@ -32,6 +32,7 @@ def _default_args(args: argparse.Namespace) -> argparse.Namespace:
 | 
			
		||||
    args.multilib = True
 | 
			
		||||
    args.packager = "John Doe <john@doe.com>"
 | 
			
		||||
    args.repository = "aur-clone"
 | 
			
		||||
    args.server = None
 | 
			
		||||
    args.sign_key = "key"
 | 
			
		||||
    args.sign_target = [SignSettings.Packages]
 | 
			
		||||
    args.web_port = 8080
 | 
			
		||||
@ -57,13 +58,34 @@ def test_run(args: argparse.Namespace, configuration: Configuration, repository:
 | 
			
		||||
    ahriman_configuration_mock.assert_called_once_with(args, "x86_64", args.repository, configuration)
 | 
			
		||||
    devtools_configuration_mock.assert_called_once_with(
 | 
			
		||||
        args.build_command, "x86_64", args.from_configuration, args.mirror, args.multilib, args.repository,
 | 
			
		||||
        repository_paths)
 | 
			
		||||
        f"file://{repository_paths.repository}")
 | 
			
		||||
    makepkg_configuration_mock.assert_called_once_with(args.packager, args.makeflags_jobs, repository_paths)
 | 
			
		||||
    sudo_configuration_mock.assert_called_once_with(repository_paths, args.build_command, "x86_64")
 | 
			
		||||
    executable_mock.assert_called_once_with(repository_paths, args.build_command, "x86_64")
 | 
			
		||||
    init_mock.assert_called_once_with()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def test_run_with_server(args: argparse.Namespace, configuration: Configuration, repository: Repository,
 | 
			
		||||
                         mocker: MockerFixture) -> None:
 | 
			
		||||
    """
 | 
			
		||||
    must run command with server specified
 | 
			
		||||
    """
 | 
			
		||||
    args = _default_args(args)
 | 
			
		||||
    args.server = "server"
 | 
			
		||||
    mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
 | 
			
		||||
    mocker.patch("ahriman.application.handlers.Setup.configuration_create_ahriman")
 | 
			
		||||
    mocker.patch("ahriman.application.handlers.Setup.configuration_create_makepkg")
 | 
			
		||||
    mocker.patch("ahriman.application.handlers.Setup.configuration_create_sudo")
 | 
			
		||||
    mocker.patch("ahriman.application.handlers.Setup.executable_create")
 | 
			
		||||
    mocker.patch("ahriman.core.alpm.repo.Repo.init")
 | 
			
		||||
    devtools_configuration_mock = mocker.patch("ahriman.application.handlers.Setup.configuration_create_devtools")
 | 
			
		||||
 | 
			
		||||
    Setup.run(args, "x86_64", configuration, report=False)
 | 
			
		||||
    devtools_configuration_mock.assert_called_once_with(
 | 
			
		||||
        args.build_command, "x86_64", args.from_configuration, args.mirror, args.multilib, args.repository,
 | 
			
		||||
        "server")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def test_build_command(args: argparse.Namespace) -> None:
 | 
			
		||||
    """
 | 
			
		||||
    must generate correct build command name
 | 
			
		||||
@ -120,8 +142,7 @@ def test_configuration_create_ahriman_no_multilib(args: argparse.Namespace, conf
 | 
			
		||||
    ])  # non-strict check called intentionally
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def test_configuration_create_devtools(args: argparse.Namespace, repository_paths: RepositoryPaths,
 | 
			
		||||
                                       mocker: MockerFixture) -> None:
 | 
			
		||||
def test_configuration_create_devtools(args: argparse.Namespace, mocker: MockerFixture) -> None:
 | 
			
		||||
    """
 | 
			
		||||
    must create configuration for the devtools
 | 
			
		||||
    """
 | 
			
		||||
@ -132,13 +153,12 @@ def test_configuration_create_devtools(args: argparse.Namespace, repository_path
 | 
			
		||||
    write_mock = mocker.patch("ahriman.core.configuration.Configuration.write")
 | 
			
		||||
 | 
			
		||||
    Setup.configuration_create_devtools(args.build_command, "x86_64", args.from_configuration,
 | 
			
		||||
                                        None, args.multilib, args.repository, repository_paths)
 | 
			
		||||
                                        None, args.multilib, args.repository, "server")
 | 
			
		||||
    add_section_mock.assert_has_calls([MockCall("multilib"), MockCall(args.repository)])
 | 
			
		||||
    write_mock.assert_called_once_with(pytest.helpers.anyvar(int))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def test_configuration_create_devtools_mirror(args: argparse.Namespace, repository_paths: RepositoryPaths,
 | 
			
		||||
                                              mocker: MockerFixture) -> None:
 | 
			
		||||
def test_configuration_create_devtools_mirror(args: argparse.Namespace, mocker: MockerFixture) -> None:
 | 
			
		||||
    """
 | 
			
		||||
    must create configuration for the devtools with mirror set explicitly
 | 
			
		||||
    """
 | 
			
		||||
@ -157,14 +177,13 @@ def test_configuration_create_devtools_mirror(args: argparse.Namespace, reposito
 | 
			
		||||
    set_option_mock = mocker.patch("ahriman.core.configuration.Configuration.set_option")
 | 
			
		||||
 | 
			
		||||
    Setup.configuration_create_devtools(args.build_command, "x86_64", args.from_configuration,
 | 
			
		||||
                                        args.mirror, False, args.repository, repository_paths)
 | 
			
		||||
                                        args.mirror, False, args.repository, "server")
 | 
			
		||||
    get_mock.assert_has_calls([MockCall("core", "Include", fallback=None), MockCall("extra", "Include", fallback=None)])
 | 
			
		||||
    remove_option_mock.assert_called_once_with("core", "Include")
 | 
			
		||||
    set_option_mock.assert_has_calls([MockCall("core", "Server", args.mirror)])  # non-strict check called intentionally
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def test_configuration_create_devtools_no_multilib(args: argparse.Namespace, repository_paths: RepositoryPaths,
 | 
			
		||||
                                                   mocker: MockerFixture) -> None:
 | 
			
		||||
def test_configuration_create_devtools_no_multilib(args: argparse.Namespace, mocker: MockerFixture) -> None:
 | 
			
		||||
    """
 | 
			
		||||
    must create configuration for the devtools without multilib
 | 
			
		||||
    """
 | 
			
		||||
@ -174,7 +193,7 @@ def test_configuration_create_devtools_no_multilib(args: argparse.Namespace, rep
 | 
			
		||||
    write_mock = mocker.patch("ahriman.core.configuration.Configuration.write")
 | 
			
		||||
 | 
			
		||||
    Setup.configuration_create_devtools(args.build_command, "x86_64", args.from_configuration,
 | 
			
		||||
                                        None, False, args.repository, repository_paths)
 | 
			
		||||
                                        None, False, args.repository, "server")
 | 
			
		||||
    write_mock.assert_called_once_with(pytest.helpers.anyvar(int))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -36,8 +36,8 @@ def test_run(args: argparse.Namespace, configuration: Configuration, repository:
 | 
			
		||||
    """
 | 
			
		||||
    args = _default_args(args)
 | 
			
		||||
    mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
 | 
			
		||||
    application_mock = mocker.patch("ahriman.core.status.client.Client.get_internal")
 | 
			
		||||
    packages_mock = mocker.patch("ahriman.core.status.client.Client.get",
 | 
			
		||||
    application_mock = mocker.patch("ahriman.core.status.client.Client.status_get")
 | 
			
		||||
    packages_mock = mocker.patch("ahriman.core.status.client.Client.package_get",
 | 
			
		||||
                                 return_value=[(package_ahriman, BuildStatus(BuildStatusEnum.Success)),
 | 
			
		||||
                                               (package_python_schedule, BuildStatus(BuildStatusEnum.Failed))])
 | 
			
		||||
    check_mock = mocker.patch("ahriman.application.handlers.Handler.check_if_empty")
 | 
			
		||||
@ -58,8 +58,8 @@ def test_run_empty_exception(args: argparse.Namespace, configuration: Configurat
 | 
			
		||||
    args = _default_args(args)
 | 
			
		||||
    args.exit_code = True
 | 
			
		||||
    mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
 | 
			
		||||
    mocker.patch("ahriman.core.status.client.Client.get_internal")
 | 
			
		||||
    mocker.patch("ahriman.core.status.client.Client.get", return_value=[])
 | 
			
		||||
    mocker.patch("ahriman.core.status.client.Client.status_get")
 | 
			
		||||
    mocker.patch("ahriman.core.status.client.Client.package_get", return_value=[])
 | 
			
		||||
    check_mock = mocker.patch("ahriman.application.handlers.Handler.check_if_empty")
 | 
			
		||||
 | 
			
		||||
    Status.run(args, "x86_64", configuration, report=False)
 | 
			
		||||
@ -74,7 +74,7 @@ def test_run_verbose(args: argparse.Namespace, configuration: Configuration, rep
 | 
			
		||||
    args = _default_args(args)
 | 
			
		||||
    args.info = True
 | 
			
		||||
    mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
 | 
			
		||||
    mocker.patch("ahriman.core.status.client.Client.get",
 | 
			
		||||
    mocker.patch("ahriman.core.status.client.Client.package_get",
 | 
			
		||||
                 return_value=[(package_ahriman, BuildStatus(BuildStatusEnum.Success))])
 | 
			
		||||
    print_mock = mocker.patch("ahriman.core.formatters.Printer.print")
 | 
			
		||||
 | 
			
		||||
@ -90,7 +90,7 @@ def test_run_with_package_filter(args: argparse.Namespace, configuration: Config
 | 
			
		||||
    args = _default_args(args)
 | 
			
		||||
    args.package = [package_ahriman.base]
 | 
			
		||||
    mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
 | 
			
		||||
    packages_mock = mocker.patch("ahriman.core.status.client.Client.get",
 | 
			
		||||
    packages_mock = mocker.patch("ahriman.core.status.client.Client.package_get",
 | 
			
		||||
                                 return_value=[(package_ahriman, BuildStatus(BuildStatusEnum.Success))])
 | 
			
		||||
 | 
			
		||||
    Status.run(args, "x86_64", configuration, report=False)
 | 
			
		||||
@ -104,7 +104,7 @@ def test_run_by_status(args: argparse.Namespace, configuration: Configuration, r
 | 
			
		||||
    """
 | 
			
		||||
    args = _default_args(args)
 | 
			
		||||
    args.status = BuildStatusEnum.Failed
 | 
			
		||||
    mocker.patch("ahriman.core.status.client.Client.get",
 | 
			
		||||
    mocker.patch("ahriman.core.status.client.Client.package_get",
 | 
			
		||||
                 return_value=[(package_ahriman, BuildStatus(BuildStatusEnum.Success)),
 | 
			
		||||
                               (package_python_schedule, BuildStatus(BuildStatusEnum.Failed))])
 | 
			
		||||
    mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
 | 
			
		||||
 | 
			
		||||
@ -34,7 +34,7 @@ def test_run(args: argparse.Namespace, configuration: Configuration, repository:
 | 
			
		||||
    """
 | 
			
		||||
    args = _default_args(args)
 | 
			
		||||
    mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
 | 
			
		||||
    update_self_mock = mocker.patch("ahriman.core.status.client.Client.update_self")
 | 
			
		||||
    update_self_mock = mocker.patch("ahriman.core.status.client.Client.status_update")
 | 
			
		||||
 | 
			
		||||
    StatusUpdate.run(args, "x86_64", configuration, report=False)
 | 
			
		||||
    update_self_mock.assert_called_once_with(args.status)
 | 
			
		||||
@ -48,7 +48,7 @@ def test_run_packages(args: argparse.Namespace, configuration: Configuration, re
 | 
			
		||||
    args = _default_args(args)
 | 
			
		||||
    args.package = [package_ahriman.base]
 | 
			
		||||
    mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
 | 
			
		||||
    update_mock = mocker.patch("ahriman.core.status.client.Client.update")
 | 
			
		||||
    update_mock = mocker.patch("ahriman.core.status.client.Client.package_update")
 | 
			
		||||
 | 
			
		||||
    StatusUpdate.run(args, "x86_64", configuration, report=False)
 | 
			
		||||
    update_mock.assert_called_once_with(package_ahriman.base, args.status)
 | 
			
		||||
@ -63,7 +63,7 @@ def test_run_remove(args: argparse.Namespace, configuration: Configuration, repo
 | 
			
		||||
    args.package = [package_ahriman.base]
 | 
			
		||||
    args.action = Action.Remove
 | 
			
		||||
    mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
 | 
			
		||||
    update_mock = mocker.patch("ahriman.core.status.client.Client.remove")
 | 
			
		||||
    update_mock = mocker.patch("ahriman.core.status.client.Client.package_remove")
 | 
			
		||||
 | 
			
		||||
    StatusUpdate.run(args, "x86_64", configuration, report=False)
 | 
			
		||||
    update_mock.assert_called_once_with(package_ahriman.base)
 | 
			
		||||
 | 
			
		||||
@ -67,8 +67,10 @@ def test_schema(configuration: Configuration) -> None:
 | 
			
		||||
    assert schema.pop("keyring-generator")
 | 
			
		||||
    assert schema.pop("mirrorlist")
 | 
			
		||||
    assert schema.pop("mirrorlist-generator")
 | 
			
		||||
    assert schema.pop("remote-call")
 | 
			
		||||
    assert schema.pop("remote-pull")
 | 
			
		||||
    assert schema.pop("remote-push")
 | 
			
		||||
    assert schema.pop("remote-service")
 | 
			
		||||
    assert schema.pop("report")
 | 
			
		||||
    assert schema.pop("rsync")
 | 
			
		||||
    assert schema.pop("s3")
 | 
			
		||||
 | 
			
		||||
@ -77,6 +77,10 @@ def test_extract_arguments(args: argparse.Namespace, configuration: Configuratio
 | 
			
		||||
    expected.extend(["--unsafe"])
 | 
			
		||||
    assert list(Web.extract_arguments(probe, "x86_64", configuration)) == expected
 | 
			
		||||
 | 
			
		||||
    configuration.set_option("web", "wait_timeout", "60")
 | 
			
		||||
    expected.extend(["--wait-timeout", "60"])
 | 
			
		||||
    assert list(Web.extract_arguments(probe, "x86_64", configuration)) == expected
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def test_extract_arguments_full(parser: argparse.ArgumentParser, configuration: Configuration):
 | 
			
		||||
    """
 | 
			
		||||
@ -91,6 +95,7 @@ def test_extract_arguments_full(parser: argparse.ArgumentParser, configuration:
 | 
			
		||||
        value = action.const or \
 | 
			
		||||
            next(iter(action.choices or []), None) or \
 | 
			
		||||
            (not action.default if isinstance(action.default, bool) else None) or \
 | 
			
		||||
            (42 if action.type == int else None) or \
 | 
			
		||||
            "random string"
 | 
			
		||||
        if action.type is not None:
 | 
			
		||||
            value = action.type(value)
 | 
			
		||||
 | 
			
		||||
@ -47,6 +47,16 @@ def test_parser_option_log_handler(parser: argparse.ArgumentParser) -> None:
 | 
			
		||||
    assert isinstance(args.log_handler, LogHandler)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def test_parser_option_wait_timeout(parser: argparse.ArgumentParser) -> None:
 | 
			
		||||
    """
 | 
			
		||||
    must convert wait-timeout option to int instance
 | 
			
		||||
    """
 | 
			
		||||
    args = parser.parse_args(["service-config"])
 | 
			
		||||
    assert isinstance(args.wait_timeout, int)
 | 
			
		||||
    args = parser.parse_args(["--wait-timeout", "60", "service-config"])
 | 
			
		||||
    assert isinstance(args.wait_timeout, int)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def test_multiple_architectures(parser: argparse.ArgumentParser) -> None:
 | 
			
		||||
    """
 | 
			
		||||
    must accept multiple architectures
 | 
			
		||||
 | 
			
		||||
@ -32,7 +32,7 @@ def test_check_version(lock: Lock, mocker: MockerFixture) -> None:
 | 
			
		||||
    """
 | 
			
		||||
    must check version correctly
 | 
			
		||||
    """
 | 
			
		||||
    mocker.patch("ahriman.core.status.client.Client.get_internal",
 | 
			
		||||
    mocker.patch("ahriman.core.status.client.Client.status_get",
 | 
			
		||||
                 return_value=InternalStatus(status=BuildStatus(), version=__version__))
 | 
			
		||||
    logging_mock = mocker.patch("logging.Logger.warning")
 | 
			
		||||
 | 
			
		||||
@ -44,7 +44,7 @@ def test_check_version_mismatch(lock: Lock, mocker: MockerFixture) -> None:
 | 
			
		||||
    """
 | 
			
		||||
    must check mismatched version correctly
 | 
			
		||||
    """
 | 
			
		||||
    mocker.patch("ahriman.core.status.client.Client.get_internal",
 | 
			
		||||
    mocker.patch("ahriman.core.status.client.Client.status_get",
 | 
			
		||||
                 return_value=InternalStatus(status=BuildStatus(), version="version"))
 | 
			
		||||
    logging_mock = mocker.patch("logging.Logger.warning")
 | 
			
		||||
 | 
			
		||||
@ -154,15 +154,35 @@ def test_create_unsafe(lock: Lock) -> None:
 | 
			
		||||
    lock.path.unlink()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def test_watch(lock: Lock, mocker: MockerFixture) -> None:
 | 
			
		||||
    """
 | 
			
		||||
    must check if lock file exists
 | 
			
		||||
    """
 | 
			
		||||
    wait_mock = mocker.patch("ahriman.models.waiter.Waiter.wait")
 | 
			
		||||
    lock.path = Path(tempfile.mktemp())  # nosec
 | 
			
		||||
 | 
			
		||||
    lock.watch()
 | 
			
		||||
    wait_mock.assert_called_once_with(lock.path.is_file)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def test_watch_skip(lock: Lock, mocker: MockerFixture) -> None:
 | 
			
		||||
    """
 | 
			
		||||
    must skip watch on empty path
 | 
			
		||||
    """
 | 
			
		||||
    mocker.patch("pathlib.Path.is_file", return_value=True)
 | 
			
		||||
    lock.watch()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def test_enter(lock: Lock, mocker: MockerFixture) -> None:
 | 
			
		||||
    """
 | 
			
		||||
    must process with context manager
 | 
			
		||||
    """
 | 
			
		||||
    check_user_mock = mocker.patch("ahriman.application.lock.Lock.check_user")
 | 
			
		||||
    check_version_mock = mocker.patch("ahriman.application.lock.Lock.check_version")
 | 
			
		||||
    watch_mock = mocker.patch("ahriman.application.lock.Lock.watch")
 | 
			
		||||
    clear_mock = mocker.patch("ahriman.application.lock.Lock.clear")
 | 
			
		||||
    create_mock = mocker.patch("ahriman.application.lock.Lock.create")
 | 
			
		||||
    update_status_mock = mocker.patch("ahriman.core.status.client.Client.update_self")
 | 
			
		||||
    update_status_mock = mocker.patch("ahriman.core.status.client.Client.status_update")
 | 
			
		||||
 | 
			
		||||
    with lock:
 | 
			
		||||
        pass
 | 
			
		||||
@ -170,6 +190,7 @@ def test_enter(lock: Lock, mocker: MockerFixture) -> None:
 | 
			
		||||
    clear_mock.assert_called_once_with()
 | 
			
		||||
    create_mock.assert_called_once_with()
 | 
			
		||||
    check_version_mock.assert_called_once_with()
 | 
			
		||||
    watch_mock.assert_called_once_with()
 | 
			
		||||
    update_status_mock.assert_has_calls([MockCall(BuildStatusEnum.Building), MockCall(BuildStatusEnum.Success)])
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -180,7 +201,7 @@ def test_exit_with_exception(lock: Lock, mocker: MockerFixture) -> None:
 | 
			
		||||
    mocker.patch("ahriman.application.lock.Lock.check_user")
 | 
			
		||||
    mocker.patch("ahriman.application.lock.Lock.clear")
 | 
			
		||||
    mocker.patch("ahriman.application.lock.Lock.create")
 | 
			
		||||
    update_status_mock = mocker.patch("ahriman.core.status.client.Client.update_self")
 | 
			
		||||
    update_status_mock = mocker.patch("ahriman.core.status.client.Client.status_update")
 | 
			
		||||
 | 
			
		||||
    with pytest.raises(Exception):
 | 
			
		||||
        with lock:
 | 
			
		||||
 | 
			
		||||
		Reference in New Issue
	
	Block a user