Exemple #1
0
async def test_export_without_environment(tmpdir, server, client, push_method):
    server_port = Config.get("client_rest_transport", "port")
    server_host = Config.get("client_rest_transport", "host", "localhost")

    result = await client.create_project("test")
    assert result.code == 200
    proj_id = result.result["project"]["id"]
    result = await client.create_environment(proj_id, "test", None, None)
    assert result.code == 200
    env_id = result.result["environment"]["id"]

    workspace = tmpdir.mkdir("tmp")
    path_main_file = workspace.join("main.cf")
    path_project_yml_file = workspace.join("project.yml")
    libs_dir = workspace.join("libs")

    path_project_yml_file.write(
        f"""
name: testproject
modulepath: {libs_dir}
downloadpath: {libs_dir}
repo: https://github.com/inmanta/
"""
    )

    path_main_file.write(
        """
vm1=std::Host(name="non-existing-machine", os=std::linux)
std::ConfigFile(host=vm1, path="/test", content="")
"""
    )

    os.chdir(workspace)

    args = [
        sys.executable,
        "-m",
        "inmanta.app",
        "export",
    ]
    args.extend(["--server_port", str(server_port)])
    args.extend(["--server_address", str(server_host)])
    args += push_method

    process = await subprocess.create_subprocess_exec(*args, stdout=sys.stdout, stderr=sys.stderr)
    try:
        await asyncio.wait_for(process.communicate(), timeout=30)
    except asyncio.TimeoutError as e:
        process.kill()
        await process.communicate()
        raise e

    # Make sure exitcode is one
    assert process.returncode == 1, f"Process ended with bad return code, got {process.returncode} (expected 1)"

    result = await client.list_versions(env_id)
    assert result.code == 200
    assert len(result.result["versions"]) == 0

    shutil.rmtree(workspace)
Exemple #2
0
    def _make_agent_config(self, env: data.Environment, agent_names: list,
                           agent_map: dict) -> str:
        """
            Generate the config file for the process that hosts the autostarted agents

            :param env: The environment for which to autostart agents
            :param agent_names: The names of the agents
            :param agent_map: The agent mapping to use
            :return: A string that contains the config file content.
        """
        environment_id = str(env.id)
        port = Config.get("server_rest_transport", "port", "8888")

        privatestatedir = os.path.join(
            Config.get("config", "state-dir", "/var/lib/inmanta"),
            environment_id)
        agent_splay = yield env.get(data.AUTOSTART_SPLAY)
        # generate config file
        config = """[config]
heartbeat-interval = 60
state-dir=%(statedir)s

agent-names = %(agents)s
environment=%(env_id)s
agent-map=%(agent_map)s
agent_splay=%(agent_splay)d

[agent_rest_transport]
port=%(port)s
host=localhost
""" % {
            "agents":
            ",".join(agent_names),
            "env_id":
            environment_id,
            "port":
            port,
            "agent_map":
            ",".join(["%s=%s" % (k, v) for (k, v) in agent_map.items()]),
            "statedir":
            privatestatedir,
            "agent_splay":
            agent_splay
        }

        if server_config.server_enable_auth:
            token = protocol.encode_token(["agent"], environment_id)
            config += """
token=%s
    """ % (token)

        ssl_cert = Config.get("server", "ssl_key_file", None)
        ssl_ca = Config.get("server", "ssl_cert_file", None)
        if ssl_ca is not None and ssl_cert is not None:
            config += """
ssl=True
ssl_ca_cert_file=%s
    """ % (ssl_ca)

        return config
Exemple #3
0
def server_token(context: Context,
                 client_types: "string[]" = ["agent"]) -> "string":
    token = Config.get("compiler_rest_transport", "token", "")
    if token == "":
        return ""

    # Request a new token for this agent
    token = ""
    try:
        client = context.get_client()

        env = Config.get("config", "environment", None)
        if env is None:
            raise Exception(
                "The environment of this model should be configured in config>environment"
            )

        def call():
            return client.create_token(tid=env,
                                       client_types=list(client_types),
                                       idempotent=True)

        result = context.run_sync(call)

        if result.code == 200:
            token = result.result["token"]
        else:
            logging.getLogger(__name__).warning("Unable to get a new token")
            raise Exception("Unable to get a valid token")
    except ConnectionRefusedError:
        logging.getLogger(__name__).exception("Unable to get a new token")
        raise Exception("Unable to get a valid token")

    return token
Exemple #4
0
    def get_environment_id(self) -> str:
        env = str(Config.get("config", "environment", None))

        if env is None:
            raise Exception(
                "The environment of the model should be configured in config>environment"
            )

        return env
Exemple #5
0
def environment() -> "string":
    """
        Return the environment id
    """
    env = str(Config.get("config", "environment", None))

    if env is None:
        raise Exception("The environment of this model should be configured in config>environment")

    return str(env)
Exemple #6
0
def getfact(context: Context, resource: "any", fact_name: "string", default_value: "any"=None) -> "any":
    """
        Retrieve a fact of the given resource
    """
    resource_id = resources.to_id(resource)
    if resource_id is None:
        raise Exception("Facts can only be retreived from resources.")

    # Special case for unit testing and mocking
    if hasattr(context.compiler, "refs") and "facts" in context.compiler.refs:
        if resource_id in context.compiler.refs["facts"] and fact_name in context.compiler.refs["facts"][resource_id]:
            return context.compiler.refs["facts"][resource_id][fact_name]

        fact_value = Unknown(source=resource)
        unknown_parameters.append({"resource": resource_id, "parameter": fact_name, "source": "fact"})

        if default_value is not None:
            return default_value
        return fact_value
    # End special case

    fact_value = None
    try:
        client = context.get_client()

        env = Config.get("config", "environment", None)
        if env is None:
            raise Exception("The environment of this model should be configured in config>environment")

        def call():
            return client.get_param(tid=env, id=fact_name, resource_id=resource_id)

        result = context.run_sync(call)

        if result.code == 200:
            fact_value = result.result["parameter"]["value"]
        else:
            logging.getLogger(__name__).info("Param %s of resource %s is unknown", fact_name, resource_id)
            fact_value = Unknown(source=resource)
            unknown_parameters.append({"resource": resource_id, "parameter": fact_name, "source": "fact"})

    except ConnectionRefusedError:
        logging.getLogger(__name__).warning("Param %s of resource %s is unknown because connection to server was refused",
                                            fact_name, resource_id)
        fact_value = Unknown(source=resource)
        unknown_parameters.append({"resource": resource_id, "parameter": fact_name, "source": "fact"})

    if isinstance(fact_value, Unknown) and default_value is not None:
        return default_value

    return fact_value
Exemple #7
0
def server_ca() -> "string":
    filename = Config.get("compiler_rest_transport", "ssl_ca_cert_file", None)
    if not filename:
        return ""

    if not os.path.isfile(filename):
        raise Exception("%s isn't a valid file" % filename)

    file_fd = open(filename, "r")
    if file_fd is None:
        raise Exception("Unable to open file %s" % filename)

    content = file_fd.read()
    return content
Exemple #8
0
def get_bind_port() -> int:
    if Config.is_set("server", "bind-port") or Config.is_set(
            "server", "bind-address"):
        # Use new bind-port option
        if Config.is_set("server_rest_transport", "port"):
            warnings.warn(
                "Ignoring the server_rest_transport.port config option since the new config options "
                "server.bind-port/server.bind-address are used.",
                category=DeprecationWarning,
            )
        return server_bind_port.get()
    else:
        # Fallback to old option
        warnings.warn(
            "The server_rest_transport.port config option is deprecated in favour of the server.bind-port option.",
            category=DeprecationWarning,
        )
        return Config.get("server_rest_transport", "port", 8888)
Exemple #9
0
def app() -> None:
    """
    Run the compiler
    """
    # Send logs to stdout
    stream_handler = _get_default_stream_handler()
    logging.root.handlers = []
    logging.root.addHandler(stream_handler)
    logging.root.setLevel(0)

    # do an initial load of known config files to build the libdir path
    Config.load_config()
    parser = cmd_parser()
    options, other = parser.parse_known_args()
    options.other = other

    # Log everything to a log_file if logfile is provided
    if options.log_file:
        watched_file_handler = _get_watched_file_handler(options)
        logging.root.addHandler(watched_file_handler)
        logging.root.removeHandler(stream_handler)
    else:
        if options.timed:
            formatter = _get_log_formatter_for_stream_handler(timed=True)
            stream_handler.setFormatter(formatter)
        log_level = _convert_to_log_level(options.verbose)
        stream_handler.setLevel(log_level)

    logging.captureWarnings(True)

    if options.config_file and not os.path.exists(options.config_file):
        LOGGER.warning("Config file %s doesn't exist", options.config_file)

    # Load the configuration
    Config.load_config(options.config_file, options.config_dir)

    if options.inmanta_version:
        print_versions_installed_components_and_exit()

    if options.warnings is not None:
        Config.set("warnings", "default", options.warnings)

    config = Config.get()
    assert isinstance(config, ConfigParser)
    WarningsManager.apply_config(config["warnings"] if "warnings" in config else None)

    # start the command
    if not hasattr(options, "func"):
        # show help
        parser.print_usage()
        return

    def report(e: BaseException) -> None:
        minus_x_set_top_level_command = options.errors
        minus_x_set_subcommand = hasattr(options, "errors_subcommand") and options.errors_subcommand
        if not minus_x_set_top_level_command and not minus_x_set_subcommand:
            if isinstance(e, CompilerException):
                print(e.format_trace(indent="  "), file=sys.stderr)
            else:
                print(str(e), file=sys.stderr)
        else:
            sys.excepthook(*sys.exc_info())

        if isinstance(e, CompilerException):
            from inmanta.compiler.help.explainer import ExplainerFactory

            helpmsg = ExplainerFactory().explain_and_format(e, plain=not _is_on_tty())
            if helpmsg is not None:
                print(helpmsg)

    try:
        options.func(options)
    except ShowUsageException as e:
        print(e.args[0], file=sys.stderr)
        parser.print_usage()
    except CLIException as e:
        report(e)
        sys.exit(e.exitcode)
    except Exception as e:
        report(e)
        sys.exit(1)
    except KeyboardInterrupt as e:
        report(e)
        sys.exit(1)
    sys.exit(0)
Exemple #10
0
async def test_export_with_specific_export_plugin(tmpvenv_active_inherit: env.VirtualEnv, tmpdir, client):
    server_port = Config.get("client_rest_transport", "port")
    server_host = Config.get("client_rest_transport", "host", "localhost")
    result = await client.create_project("test")
    assert result.code == 200
    proj_id = result.result["project"]["id"]
    result = await client.create_environment(proj_id, "test", None, None)
    assert result.code == 200
    env_id = result.result["environment"]["id"]
    workspace = tmpdir.mkdir("tmp")
    libs_dir = workspace.join("libs")

    # project.yml
    path_project_yml_file = workspace.join("project.yml")
    path_project_yml_file.write(
        f"""
name: testproject
modulepath: {libs_dir}
downloadpath: {libs_dir}
repo: https://github.com/inmanta/
"""
    )

    # main.cf
    path_main_file = workspace.join("main.cf")
    path_main_file.write("import test")

    # test module
    module_dir = libs_dir.join("test")
    os.makedirs(module_dir)

    # Module.yml
    module_yml_file = module_dir.join("module.yml")
    module_yml_file.write(
        """
name: test
license: test
version: 1.0.0
    """
    )

    # .inmanta
    dot_inmanta_cfg_file = workspace.join(".inmanta")
    dot_inmanta_cfg_file.write(
        """
[config]
export=other_exporter
    """
    )

    # plugin/__init__.py
    plugins_dir = module_dir.join("plugins")
    os.makedirs(plugins_dir)
    init_file = plugins_dir.join("__init__.py")
    init_file.write(
        """
from inmanta.export import export, Exporter

@export("test_exporter")
def test_exporter(exporter: Exporter) -> None:
    print("test_exporter ran")

@export("other_exporter")
def other_exporter(exporter: Exporter) -> None:
    print("other_exporter ran")
    """
    )

    # model/_init.cf
    model_dir = module_dir.join("model")
    os.makedirs(model_dir)
    init_cf_file = model_dir.join("_init.cf")
    init_cf_file.write("")

    await install_project(tmpvenv_active_inherit, workspace)

    os.chdir(workspace)

    args = [
        tmpvenv_active_inherit.python_path,
        "-m",
        "inmanta.app",
        "export",
        "--export-plugin",
        "test_exporter",
        "-e",
        str(env_id),
        "--server_port",
        str(server_port),
        "--server_address",
        str(server_host),
    ]

    process = await subprocess.create_subprocess_exec(*args, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    try:
        (stdout, stderr) = await asyncio.wait_for(process.communicate(), timeout=30)
    except asyncio.TimeoutError as e:
        process.kill()
        await process.communicate()
        raise e

    # Make sure exitcode is zero
    assert process.returncode == 0

    assert "test_exporter ran" in stdout.decode("utf-8")
    assert "other_exporter" not in stdout.decode("utf-8")

    # ## Failure

    path_main_file.write(
        """import test

vm1=std::Host(name="non-existing-machine", os=std::linux)

vm1.name = "other"
"""
    )

    process = await subprocess.create_subprocess_exec(*args, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    try:
        (stdout, stderr) = await asyncio.wait_for(process.communicate(), timeout=30)
    except asyncio.TimeoutError as e:
        process.kill()
        await process.communicate()
        raise e

    # Make sure exitcode is one
    assert process.returncode == 1

    assert "test_exporter ran" not in stdout.decode("utf-8")
    assert "other_exporter" not in stdout.decode("utf-8")

    shutil.rmtree(workspace)
Exemple #11
0
async def test_export(tmpvenv_active_inherit: env.VirtualEnv, tmpdir, server, client, push_method, set_server, set_port):
    server_port = Config.get("client_rest_transport", "port")
    server_host = Config.get("client_rest_transport", "host", "localhost")

    result = await client.create_project("test")
    assert result.code == 200
    proj_id = result.result["project"]["id"]
    result = await client.create_environment(proj_id, "test", None, None)
    assert result.code == 200
    env_id = result.result["environment"]["id"]

    workspace = tmpdir.mkdir("tmp")
    path_main_file = workspace.join("main.cf")
    path_project_yml_file = workspace.join("project.yml")
    path_config_file = workspace.join(".inmanta")
    libs_dir = workspace.join("libs")

    path_project_yml_file.write(
        f"""
name: testproject
modulepath: {libs_dir}
downloadpath: {libs_dir}
repo: https://github.com/inmanta/
"""
    )

    path_main_file.write(
        """
vm1=std::Host(name="non-existing-machine", os=std::linux)
std::ConfigFile(host=vm1, path="/test", content="")
"""
    )

    path_config_file.write(
        f"""
[compiler_rest_transport]
{'host=' + server_host if not set_server else ''}
{'port=' + str(server_port) if not set_port else ''}

[cmdline_rest_transport]
{'host=' + server_host if not set_server else ''}
{'port=' + str(server_port) if not set_port else ''}
"""
    )

    await install_project(tmpvenv_active_inherit, workspace)

    os.chdir(workspace)

    args = [
        sys.executable,
        "-m",
        "inmanta.app",
        "export",
        "-e",
        str(env_id),
    ]
    if set_port:
        args.extend(["--server_port", str(server_port)])
    if set_server:
        args.extend(["--server_address", str(server_host)])
    args += push_method

    process = await subprocess.create_subprocess_exec(*args, stdout=sys.stdout, stderr=sys.stderr)
    try:
        await asyncio.wait_for(process.communicate(), timeout=30)
    except asyncio.TimeoutError as e:
        process.kill()
        await process.communicate()
        raise e

    # Make sure exitcode is zero
    assert process.returncode == 0, f"Process ended with bad return code, got {process.returncode} (expected 0)"

    result = await client.list_versions(env_id)
    assert result.code == 200
    assert len(result.result["versions"]) == 1

    details_exported_version = result.result["versions"][0]

    assert details_exported_version["result"] == VersionState.deploying.name

    shutil.rmtree(workspace)
Exemple #12
0
def test_configfile_hierarchy(monkeypatch, tmpdir):
    etc_inmanta_dir = os.path.join(tmpdir, "etc", "inmanta")
    os.makedirs(etc_inmanta_dir, exist_ok=False)

    main_inmanta_cfg_file = os.path.join(etc_inmanta_dir, "inmanta.cfg")

    inmanta_d_dir = os.path.join(etc_inmanta_dir, "inmanta.d")
    os.mkdir(inmanta_d_dir)

    inmanta_d_cfg_file01 = os.path.join(inmanta_d_dir, "01-dbconfig.cfg")
    inmanta_d_cfg_file02 = os.path.join(inmanta_d_dir, "02-dbconfig.cfg")
    inmanta_d_cfg_file_no_cfg_extension = os.path.join(inmanta_d_dir,
                                                       "03-config")

    dot_inmanta_file = os.path.join(tmpdir, ".inmanta")
    dot_inmanta_cfg_file = os.path.join(tmpdir, ".inmanta.cfg")

    min_c_file = os.path.join(tmpdir, "custom.cfg")

    monkeypatch.setenv("INMANTA_SERVER_AUTH", "true")
    monkeypatch.setenv("INMANTA_SERVER_AGENT_TIMEOUT", "60")

    with open(main_inmanta_cfg_file, "w", encoding="utf-8") as f:
        f.write("""
[server]
auth=false
[config]
log-dir=/log
[database]
host=host1
name=db1
port=1234
[influxdb]
host=host1
interval=10
tags=tag1=value1
[dashboard]
path=/some/directory
client-id=test
        """)

    with open(inmanta_d_cfg_file01, "w", encoding="utf-8") as f:
        f.write("""
[database]
host=host2
name=db2
[influxdb]
host=host2
        """)

    with open(inmanta_d_cfg_file02, "w", encoding="utf-8") as f:
        f.write("""
[database]
port=5678
[influxdb]
host=host3
interval=20
        """)

    with open(inmanta_d_cfg_file_no_cfg_extension, "w", encoding="utf-8") as f:
        f.write("""
[database]
port=9999
        """)

    with open(dot_inmanta_file, "w", encoding="utf-8") as f:
        f.write("""
[database]
host=host3
[influxdb]
tags=tag2=value2
[dashboard]
path=/some/other/directory
        """)

    with open(dot_inmanta_cfg_file, "w", encoding="utf-8") as f:
        f.write("""
[dashboard]
path=/directory
client-id=test123
        """)

    with open(min_c_file, "w", encoding="utf-8") as f:
        f.write("""
[dashboard]
client-id=test456
        """)

    os.chdir(tmpdir)
    Config.load_config(min_c_config_file=min_c_file,
                       config_dir=inmanta_d_dir,
                       main_cfg_file=main_inmanta_cfg_file)

    assert Config.get("config", "log-dir") == "/log"
    assert Config.get("database", "host") == "host3"
    assert Config.get("database", "name") == "db2"
    assert Config.get("database", "port") == 5678
    assert Config.get("influxdb", "host") == "host3"
    assert Config.get("influxdb", "interval") == 20
    assert Config.get("influxdb", "tags")["tag2"] == "value2"
    assert Config.get("dashboard", "path") == "/directory"
    assert Config.get("dashboard", "client-id") == "test456"
    assert Config.get("server", "auth")
    assert Config.get("server", "agent-timeout") == 60
Exemple #13
0
def server_port() -> "number":
    return Config.get("compiler_rest_transport", "port", 8888)
Exemple #14
0
def server_ssl() -> "bool":
    return Config.get("compiler_rest_transport", "ssl", False)