def test_module_requires(inmanta_module_v1):
    inmanta_module_v1.write_metadata_file("""
name: mod
license: ASL
version: 1.0.0
requires:
    - std
    - ip > 1.0.0
        """)
    mod: module.Module = module.ModuleV1(
        None, inmanta_module_v1.get_root_dir_of_module())
    assert mod.requires() == [
        InmantaModuleRequirement.parse("std"),
        InmantaModuleRequirement.parse("ip > 1.0.0")
    ]
Exemple #2
0
def test_module_unload(
    local_module_package_index: str,
    snippetcompiler,
) -> None:
    """
    Verify that Module.unload removes it from the project, unloads its Python modules and deregisters its plugins.
    """
    project: Project = snippetcompiler.setup_for_snippet(
        """
import minimalv2module
import elaboratev2module
        """.strip(),
        python_package_sources=[local_module_package_index],
        python_requires=[
            ModuleV2Source.get_python_package_requirement(
                InmantaModuleRequirement.parse("minimalv2module")),
            ModuleV2Source.get_python_package_requirement(
                InmantaModuleRequirement.parse("elaboratev2module")),
        ],
        autostd=False,
    )
    project.load()

    assert "minimalv2module" in project.modules
    assert "elaboratev2module" in project.modules

    assert "inmanta_plugins.minimalv2module" in sys.modules
    assert "inmanta_plugins.elaboratev2module" in sys.modules

    assert "elaboratev2module::print_message" in plugins.PluginMeta.get_functions(
    )

    project.modules["elaboratev2module"].unload()

    assert "minimalv2module" in project.modules
    assert "elaboratev2module" not in project.modules

    assert "inmanta_plugins.minimalv2module" in sys.modules
    assert "inmanta_plugins.elaboratev2module" not in sys.modules

    assert "elaboratev2module::print_message" not in plugins.PluginMeta.get_functions(
    )
Exemple #3
0
 def load(requires: Optional[List[Requirement]] = None) -> None:
     project: Project = snippetcompiler_clean.setup_for_snippet(
         f"import {module_name}",
         autostd=False,
         install_project=False,
         python_package_sources=[local_module_package_index],
         # make sure that even listing the requirement in project.yml does not suffice
         project_requires=[InmantaModuleRequirement.parse(module_name)],
         python_requires=requires,
     )
     project.load_module_recursive(install=True)
def test_module_requires_legacy(inmanta_module_v1):
    inmanta_module_v1.write_metadata_file("""
name: mod
license: ASL
version: 1.0.0
requires:
    std: std
    ip: ip > 1.0.0
        """)
    mod: module.Module
    with warnings.catch_warnings(record=True) as w:
        mod = module.ModuleV1(None, inmanta_module_v1.get_root_dir_of_module())
        assert len(w) == 1
        warning = w[0]
        assert issubclass(warning.category, MetadataDeprecationWarning)
        assert "yaml dictionary syntax for specifying module requirements has been deprecated" in str(
            warning.message)
    assert mod.requires() == [
        InmantaModuleRequirement.parse("std"),
        InmantaModuleRequirement.parse("ip > 1.0.0")
    ]
Exemple #5
0
    def add(self,
            module_req: str,
            v1: bool = False,
            v2: bool = False,
            override: bool = False) -> None:
        """
        Add a module dependency to an Inmanta module or project.

        :param module_req: The module to add, optionally with a version constraint.
        :param v1: Whether the given module should be added as a V1 module or not.
        :param override: If set to True, override the version constraint when the module dependency already exists.
                         If set to False, this method raises an exception when the module dependency already exists.
        """
        if not v1 and not v2:
            raise CLIException("Either --v1 or --v2 has to be set", exitcode=1)
        if v1 and v2:
            raise CLIException("--v1 and --v2 cannot be set together",
                               exitcode=1)
        module_like: Optional[ModuleLike] = ModuleLike.from_path(
            path=os.getcwd())
        if module_like is None:
            raise CLIException(
                "Current working directory doesn't contain an Inmanta module or project",
                exitcode=1)
        try:
            module_requirement = InmantaModuleRequirement.parse(module_req)
        except InvalidRequirement:
            raise CLIException(f"'{module_req}' is not a valid requirement",
                               exitcode=1)
        if not override and module_like.has_module_requirement(
                module_requirement.key):
            raise CLIException(
                "A dependency on the given module was already defined, use --override to override the version constraint",
                exitcode=1,
            )
        if isinstance(module_like, Project):
            try:
                module_like.install_module(module_requirement,
                                           install_as_v1_module=v1)
            except ModuleNotFoundException:
                raise CLIException(
                    f"Failed to install {module_requirement} as a {'v1' if v1 else 'v2'} module.",
                    exitcode=1,
                )
            else:
                # cached project might have inconsistent state after modifying the environment through another instance
                self.get_project(load=False).invalidate_state()
        module_like.add_module_requirement_persistent(
            requirement=module_requirement, add_as_v1_module=v1)
Exemple #6
0
def test_install_module_no_v2_source(snippetcompiler) -> None:
    """
    Verify that attempting to install a v2 module without a v2 module source configured results in the appropriate exception.
    """
    module_name = "non_existing_module"
    with pytest.raises(Exception) as e:
        snippetcompiler.setup_for_snippet(
            snippet=f"import {module_name}",
            install_project=True,
            python_package_sources=[],
            python_requires=[
                ModuleV2Source.get_python_package_requirement(
                    InmantaModuleRequirement.parse(module_name)),
            ],
        )
    message: str = (
        "Attempting to install a v2 module but no v2 module source is configured. Add at least one repo of type"
        ' "package" to the project config file.')
    assert message in e.value.format_trace()
Exemple #7
0
def test_module_has_v2_requirements_on_non_imported_module(
        snippetcompiler, local_module_package_index: str) -> None:
    """
    A module has a module V2 requirement on a module that it doesn't import.
    Ensure that the non-imported module is not loaded.

    Scenario: module dependency_but_no_import has dependency on minimalv2module
              but dependency_but_no_import doesn't import minimalv2module.
    """
    project: Project = snippetcompiler.setup_for_snippet(
        snippet="import dependency_but_no_import",
        python_package_sources=[local_module_package_index],
        python_requires=[
            ModuleV2Source.get_python_package_requirement(
                InmantaModuleRequirement.parse("dependency_but_no_import")),
        ],
        autostd=False,
    )
    project.load_module_recursive()
    assert "minimalv2module" not in project.modules
Exemple #8
0
def test_project_has_v2_requirements_on_non_imported_module(
    snippetcompiler,
    local_module_package_index: str,
) -> None:
    """
    A Project has a module V2 requirement on a module that it doesn't import.
    Ensure that the non-imported module is not loaded.
    """
    dependency = "elaboratev2module"
    project: Project = snippetcompiler.setup_for_snippet(
        snippet="",  # Don't import elaboratev2module
        python_package_sources=[local_module_package_index],
        python_requires=[
            ModuleV2Source.get_python_package_requirement(
                InmantaModuleRequirement.parse(dependency)),
        ],
        autostd=False,
    )
    project.load_module_recursive()
    assert dependency not in project.modules
Exemple #9
0
def test_load_import_based_v2_module(
    local_module_package_index: str,
    snippetcompiler_clean,
    modules_dir: str,
    modules_v2_dir: str,
    tmpdir: py.path.local,
    v1: bool,
    explicit_dependency: bool,
) -> None:
    """
    A module needs to explicitly list its v2 dependencies in order to be able to load them. Import-based loading is not
    allowed.
    """
    main_module_name: str = "mymodule"
    dependency_module_name: str = "minimalv2module"
    index: PipIndex = PipIndex(os.path.join(str(tmpdir), ".my-index"))
    libs_dir: str = os.path.join(str(tmpdir), "libs")
    os.makedirs(libs_dir)

    model: str = f"import {dependency_module_name}"
    requirements: List[InmantaModuleRequirement] = ([
        InmantaModuleRequirement.parse(dependency_module_name)
    ] if explicit_dependency else [])

    if v1:
        v1_module_from_template(
            os.path.join(modules_dir, "minimalv1module"),
            os.path.join(libs_dir, main_module_name),
            new_name=main_module_name,
            new_content_init_cf=model,
            new_requirements=requirements,
        )
    else:
        module_from_template(
            os.path.join(modules_v2_dir, "minimalv2module"),
            os.path.join(str(tmpdir), main_module_name),
            new_name=main_module_name,
            new_content_init_cf=model,
            new_requirements=requirements,
            install=False,
            publish_index=index,
        )

    project: Project = snippetcompiler_clean.setup_for_snippet(
        f"import {main_module_name}",
        autostd=False,
        install_project=False,
        add_to_module_path=[libs_dir],
        python_package_sources=[local_module_package_index, index.url],
        # make sure that even listing the requirement in project.yml does not suffice
        project_requires=[
            InmantaModuleRequirement.parse(dependency_module_name)
        ],
        python_requires=[] if v1 else [
            Requirement.parse(
                ModuleV2Source.get_package_name_for(main_module_name))
        ],
    )

    if explicit_dependency:
        # assert that it doesn't raise an error with explicit requirements set
        project.load_module_recursive(install=True)
    else:
        with pytest.raises(
                ModuleLoadingException,
                match=f"Failed to load module {dependency_module_name}"):
            project.load_module_recursive(install=True)
Exemple #10
0
        needs_versioning_warning=False,
        install_v2_modules=[
            LocalPackagePath(path=module_copy_dir, editable=True)
        ],
    )


@pytest.mark.parametrize(
    "v1_module, all_python_requirements,strict_python_requirements,module_requirements,module_v2_requirements",
    [
        (
            True,
            ["jinja2~=3.2.1", "inmanta-module-v2-module==1.2.3"],
            ["jinja2~=3.2.1"],
            ["v2_module==1.2.3", "v1_module==1.1.1"],
            [InmantaModuleRequirement.parse("v2_module==1.2.3")],
        ),
        (
            False,
            ["jinja2~=3.2.1", "inmanta-module-v2-module==1.2.3"],
            ["jinja2~=3.2.1"],
            ["v2_module==1.2.3"],
            [InmantaModuleRequirement.parse("v2_module==1.2.3")],
        ),
    ],
)
def test_get_requirements(
    modules_dir: str,
    modules_v2_dir: str,
    v1_module: bool,
    all_python_requirements: List[str],
Exemple #11
0
def test_module_update_with_v2_module(
    tmpdir: py.path.local,
    modules_v2_dir: str,
    snippetcompiler_clean,
    modules_repo: str,
    corrupt_module: bool,
    install_mode: InstallMode,
) -> None:
    """
    Assert that the `inmanta module update` command works correctly when executed on a project with a V2 module.

    :param corrupt_module: Whether the module to be updated contains a syntax error or not.

    Dependency graph:

        -> Inmanta project
            -> module1 (v2) -> module2 (v2)
            -> mod11 (v1)
    """
    template_module_name = "elaboratev2module"
    template_module_dir = os.path.join(modules_v2_dir, template_module_name)

    pip_index = PipIndex(artifact_dir=os.path.join(str(tmpdir), "pip-index"))

    def assert_version_installed(module_name: str, version: str) -> None:
        package_name = ModuleV2Source.get_package_name_for(module_name)
        installed_packages: Dict[
            str, Version] = process_env.get_installed_packages()
        assert package_name in installed_packages
        assert str(installed_packages[package_name]) == version

    for module_name, versions in {
            "module2": ["2.0.1", "2.1.0", "2.2.0", "2.2.1.dev0", "3.0.1"],
            "module1": ["1.2.4", "1.2.5"],
    }.items():
        # Module1 only has patch updates and Module2 has minor and major updates.
        # This ensure that the test covers the different types of version bumps.
        for current_version in versions:
            module_dir = os.path.join(tmpdir,
                                      f"{module_name}-v{current_version}")
            module_from_template(
                source_dir=template_module_dir,
                dest_dir=module_dir,
                new_version=Version(current_version),
                new_name=module_name,
                new_requirements=[
                    InmantaModuleRequirement(
                        Requirement.parse("module2<3.0.0"))
                ] if module_name == "module1" else None,
                install=False,
                publish_index=pip_index,
                new_content_init_cf="import module2"
                if module_name == "module1" else None,
            )
    patched_module_dir = os.path.join(tmpdir, "module1-v1.2.3")
    module_from_template(
        source_dir=template_module_dir,
        dest_dir=patched_module_dir,
        new_version=Version("1.2.3"),
        new_name="module1",
        # Add a dependency on module2, without setting an explicit version constraint. Later version of module1
        # do set a version constraint on the dependency on module2. This way it is verified whether the module update
        # command takes into account the version constraints set in a new version of a module.
        new_requirements=[
            InmantaModuleRequirement(Requirement.parse("module2"))
        ],
        install=False,
        publish_index=pip_index,
        new_content_init_cf="entity"
        if corrupt_module else None,  # Introduce syntax error in the module
    )

    module_path = os.path.join(tmpdir, "modulepath")
    os.mkdir(module_path)
    mod11_dir = clone_repo(modules_repo, "mod11", module_path, tag="3.2.1")

    snippetcompiler_clean.setup_for_snippet(
        # Don't import module2 in the inmanta project. An import for module2 is present in module1 instead.
        # This tests whether the module update command takes into account all transitive dependencies.
        snippet="""
        import module1
        import mod11
        """,
        autostd=False,
        install_v2_modules=[
            LocalPackagePath(path=os.path.join(tmpdir, "module2-v2.0.1")),
            LocalPackagePath(path=patched_module_dir),
        ],
        add_to_module_path=[module_path],
        python_package_sources=[pip_index.url],
        project_requires=[
            InmantaModuleRequirement.parse("module1<1.2.5"),
            InmantaModuleRequirement.parse("mod11<4.2.0"),
        ],
        install_mode=install_mode,
        install_project=False,
    )

    assert_version_installed(module_name="module1", version="1.2.3")
    assert_version_installed(module_name="module2", version="2.0.1")
    assert ModuleV1(project=None, path=mod11_dir).version == Version("3.2.1")
    ModuleTool().update()
    assert_version_installed(module_name="module1", version="1.2.4")
    assert_version_installed(module_name="module2",
                             version="2.2.0" if install_mode
                             == InstallMode.release else "2.2.1.dev0")
    assert ModuleV1(project=None, path=mod11_dir).version == Version("4.1.2")