From d0835017ac0b986730ed722ae8f6c0b98c9a92d1 Mon Sep 17 00:00:00 2001 From: Lejoly Florent Date: Thu, 4 Aug 2022 13:34:06 +0200 Subject: [PATCH] add the possibility to add extra settings to the env from outside of inmanta-core (Issue inmanta/inmanta-lsm#927, PR #4603) Pull request opened by the merge tool on behalf of #4603 --- .../unreleased/927-add-env-settings.yml | 5 ++ src/inmanta/data/__init__.py | 13 +++ .../server/services/environmentservice.py | 15 +++- tests/agent_server/test_server_agent.py | 29 ++++++- tests/server/test_env_settings.py | 86 +++++++++++++++++++ 5 files changed, 144 insertions(+), 4 deletions(-) create mode 100644 changelogs/unreleased/927-add-env-settings.yml diff --git a/changelogs/unreleased/927-add-env-settings.yml b/changelogs/unreleased/927-add-env-settings.yml new file mode 100644 index 0000000000..602b3ee16d --- /dev/null +++ b/changelogs/unreleased/927-add-env-settings.yml @@ -0,0 +1,5 @@ +description: add the possibility to add extra settings to the env from outside of inmanta-core +issue-nr: 927 +issue-repo: inmanta-lsm +change-type: minor +destination-branches: [master, iso5] diff --git a/src/inmanta/data/__init__.py b/src/inmanta/data/__init__.py index 62b33aed00..f952b189aa 100644 --- a/src/inmanta/data/__init__.py +++ b/src/inmanta/data/__init__.py @@ -2401,6 +2401,19 @@ async def get_next_version(self) -> int: self.last_version = version return version + @classmethod + async def register_setting(cls, setting: Setting) -> None: + """ + Adds a new setting to the environments from outside inmanta-core. + As example, inmanta-lsm can use this method to add settings that are only + relevant for inmanta-lsm but that are needed in the environments. + + :param setting: the setting that should be added to the existing settings + """ + if setting.name in cls._settings: + raise KeyError() + cls._settings[setting.name] = setting + @classmethod async def get_list( cls: Type[TBaseDocument], diff --git a/src/inmanta/server/services/environmentservice.py b/src/inmanta/server/services/environmentservice.py index 47590af8f6..ff450844e5 100644 --- a/src/inmanta/server/services/environmentservice.py +++ b/src/inmanta/server/services/environmentservice.py @@ -31,7 +31,7 @@ from asyncpg import StringDataRightTruncationError from inmanta import config, data -from inmanta.data import model +from inmanta.data import Setting, model from inmanta.protocol import encode_token, handle, methods, methods_v2 from inmanta.protocol.common import ReturnValue, attach_warnings from inmanta.protocol.exceptions import BadRequest, Forbidden, NotFound, ServerError @@ -305,7 +305,8 @@ async def create_token(self, env: data.Environment, client_types: List[str], ide @handle(methods.list_settings, env="tid") async def list_settings(self, env: data.Environment) -> Apireturn: - return 200, {"settings": env.settings, "metadata": data.Environment._settings} + settings = {k: env.settings[k] for k in env.settings.keys() if k in data.Environment._settings.keys()} + return 200, {"settings": settings, "metadata": data.Environment._settings} @handle(methods.set_setting, env="tid", key="id") async def set_setting(self, env: data.Environment, key: str, value: model.EnvSettingType) -> Apireturn: @@ -601,3 +602,13 @@ async def notify_listeners( await listener.environment_action_updated(updated_env, original_env) except Exception: LOGGER.warning(f"Notifying listener of {action} failed with the following exception", exc_info=True) + + async def register_setting(self, setting: Setting) -> None: + """ + Should only be called during pre-start + Adds a new setting to the environments from outside inmanta-core. + As example, inmanta-lsm can use this method to add settings that are only + relevant for inmanta-lsm but that are needed in the environment. + :param setting: the setting that should be added to the existing settings + """ + await data.Environment.register_setting(setting) diff --git a/tests/agent_server/test_server_agent.py b/tests/agent_server/test_server_agent.py index 6c08913eeb..44a62648f0 100644 --- a/tests/agent_server/test_server_agent.py +++ b/tests/agent_server/test_server_agent.py @@ -34,9 +34,16 @@ from inmanta.ast import CompilerException from inmanta.config import Config from inmanta.const import AgentAction, AgentStatus, ParameterSource, ResourceState -from inmanta.data import ENVIRONMENT_AGENT_TRIGGER_METHOD -from inmanta.server import SLICE_AGENT_MANAGER, SLICE_AUTOSTARTED_AGENT_MANAGER, SLICE_PARAM, SLICE_SESSION_MANAGER +from inmanta.data import ENVIRONMENT_AGENT_TRIGGER_METHOD, Setting, convert_boolean +from inmanta.server import ( + SLICE_AGENT_MANAGER, + SLICE_AUTOSTARTED_AGENT_MANAGER, + SLICE_ENVIRONMENT, + SLICE_PARAM, + SLICE_SESSION_MANAGER, +) from inmanta.server.bootloader import InmantaBootloader +from inmanta.server.services.environmentservice import EnvironmentService from inmanta.util import get_compiler_version from utils import ( UNKWN, @@ -719,6 +726,24 @@ async def test_get_set_param(resource_container, environment, client, server): assert result.code == 200 +async def test_register_setting(environment, client, server): + """ + Test registering a new setting. + """ + new_setting: Setting = Setting( + name="a new boolean setting", + default=False, + typ="bool", + validator=convert_boolean, + doc="a new setting", + ) + env_slice: EnvironmentService = server.get_slice(SLICE_ENVIRONMENT) + await env_slice.register_setting(new_setting) + result = await client.get_setting(tid=environment, id="a new boolean setting") + assert result.code == 200 + assert result.result["value"] is False + + async def test_unkown_parameters(resource_container, environment, client, server, clienthelper, agent, no_agent_backoff): """ Test retrieving facts from the agent diff --git a/tests/server/test_env_settings.py b/tests/server/test_env_settings.py index 9314e030ca..d512958d1b 100644 --- a/tests/server/test_env_settings.py +++ b/tests/server/test_env_settings.py @@ -16,8 +16,12 @@ Contact: code@inmanta.com """ from typing import Dict +from uuid import UUID + +import pytest from inmanta import data +from inmanta.data import Environment, Setting, convert_boolean from inmanta.util import get_compiler_version @@ -311,3 +315,85 @@ async def test_default_value_purge_on_delete_setting(server, client): result = await client.get_setting(tid=env_id, id=data.PURGE_ON_DELETE) assert result.code == 200 assert result.result["value"] is False + + +async def test_environment_add_new_setting_parameter(server, client, environment): + new_setting: Setting = Setting( + name="a new setting", + default=False, + typ="bool", + validator=convert_boolean, + doc="a new setting", + ) + + await data.Environment.register_setting(new_setting) + + result = await client.get_setting(tid=environment, id="a new setting") + assert result.code == 200 + assert result.result["value"] is False + + result = await client.set_setting(tid=environment, id="a new setting", value=True) + assert result.code == 200 + + result = await client.get_setting(tid=environment, id="a new setting") + assert result.code == 200 + assert result.result["value"] is True + + result = await client.get_setting(tid=environment, id=data.AUTO_DEPLOY) + assert result.code == 200 + assert result.result["value"] is False + + existing_setting: Setting = Setting( + name=data.AUTO_DEPLOY, + default=False, + typ="bool", + validator=convert_boolean, + doc="an existing setting", + ) + with pytest.raises(KeyError): + await data.Environment.register_setting(existing_setting) + + result = await client.get_setting(tid=environment, id=data.AUTO_DEPLOY) + assert result.code == 200 + assert result.result["value"] is False + + +async def test_get_setting_no_longer_exist(server, client, environment): + """ + Test what happens when a setting exists in the database for which the definition no longer exists + """ + env_id = UUID(environment) + env = await data.Environment.get_by_id(env_id) + project_id = env.project + setting_db_query = ( + "UPDATE environment SET settings=jsonb_set(settings, $1::text[], " + "to_jsonb($2::boolean), TRUE) WHERE name=$3 AND project=$4" + ) + values = [["new_setting"], True, "dev", project_id] + await Environment._execute_query(setting_db_query, *values) + + result = await client.get_setting(tid=environment, id="a setting") + assert result.code == 404 + assert result.result["message"] == "Request or referenced resource does not exist" + + result = await client.list_settings(tid=environment) + assert result.code == 200 + assert "new_setting" not in result.result["settings"].keys() + + new_setting: Setting = Setting( + name="new_setting", + default=False, + typ="bool", + validator=convert_boolean, + doc="new_setting", + ) + + await data.Environment.register_setting(new_setting) + + result = await client.get_setting(tid=environment, id="new_setting") + assert result.code == 200 + assert result.result["value"] is True + + result = await client.list_settings(tid=environment) + assert result.code == 200 + assert "new_setting" in result.result["settings"].keys()