From 446e95ec9a16b5f686ddba77baf1582643e01ee0 Mon Sep 17 00:00:00 2001 From: Wouter De Borger Date: Tue, 25 Feb 2025 11:27:54 +0100 Subject: [PATCH] Issue/582 reference (#583) * Added create_environment_reference plugin --- CHANGELOG.md | 2 ++ model/testing.cf | 4 ++- plugins/__init__.py | 38 +++++++++++++++++++++++++++++ plugins/resources.py | 3 ++- tests/test_reference.py | 54 +++++++++++++++++++++++++++++++++++++++++ 5 files changed, 99 insertions(+), 2 deletions(-) create mode 100644 tests/test_reference.py diff --git a/CHANGELOG.md b/CHANGELOG.md index 01d211ed..a04a02d4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,8 @@ ## v8.3.0 - ? +- Added create_environment_reference plugin to create reference to environment variables +- Added the 'value' attribute to std::testing::NullResource - Log warning when environment variable is not found by get_env plugin - Deprecate get_env_int plugin in favor of int(get_env()) diff --git a/model/testing.cf b/model/testing.cf index b4ab9715..e8dff1af 100644 --- a/model/testing.cf +++ b/model/testing.cf @@ -21,15 +21,17 @@ entity NullResource extends ManagedResource, PurgeableResource: A resource that does nothing, for use in tests and examples :attr name: the name of this resource + :attr value: a value this resource can cary, has no purpose :attr agentname: the name of the agent to deploy this resource on :attr fail: when true, this resource will always fail on both dryrun and deploy """ string name = "null" string agentname = "internal" + string value = "" bool send_event = true bool fail = false end index NullResource(agentname, name) -implement NullResource using std::none \ No newline at end of file +implement NullResource using std::none diff --git a/plugins/__init__.py b/plugins/__init__.py index 631386d1..b9575940 100644 --- a/plugins/__init__.py +++ b/plugins/__init__.py @@ -41,6 +41,7 @@ # don't bind to `resources` because this package has a submodule named resources that will bind to `resources` when imported import inmanta.resources from inmanta import util +from inmanta.agent.handler import LoggerABC from inmanta.ast import NotFoundException, OptionalValueException, RuntimeException from inmanta.config import Config from inmanta.execute.proxy import DynamicProxy, UnknownException @@ -1313,3 +1314,40 @@ def json_dumps(obj: "any") -> "string": :param obj: The inmanta object that should be serialized as json. """ return json.dumps(obj, default=util.internal_json_encoder) + + +try: + from inmanta.references import Reference, reference + + @reference("std::Environment") + class EnvironmentReference(Reference[str]): + """A reference to fetch environment variables""" + + def __init__(self, name: str | Reference[str]) -> None: + """ + :param name: The name of the environment variable. + """ + super().__init__() + self.name = name + + def resolve(self, logger: LoggerABC) -> str: + """Resolve the reference""" + env_var_name = self.resolve_other(self.name, logger) + logger.debug("Resolving environment variable %(name)s", name=self.name) + value = os.getenv(env_var_name) + if value is None: + raise LookupError(f"Environment variable {env_var_name} is not set") + return value + + @plugin + def create_environment_reference(name: str | Reference[str]) -> Reference[str]: + """Create an environment reference + + :param name: The name of the variable to fetch from the environment + :return: A reference to what can be resolved to a string + """ + return EnvironmentReference(name=name) + +except ImportError: + # Reference are not yet supported by this core version + pass diff --git a/plugins/resources.py b/plugins/resources.py index 261cae20..e26b9c24 100644 --- a/plugins/resources.py +++ b/plugins/resources.py @@ -32,7 +32,7 @@ @resource("std::testing::NullResource", agent="agentname", id_attribute="name") class Null(ManagedResource, PurgeableResource): - fields = ("name", "agentname", "fail") + fields = ("name", "agentname", "fail", "value") @resource("std::AgentConfig", agent="agent", id_attribute="agentname") @@ -61,6 +61,7 @@ class NullProvider(CRUDHandler): def read_resource(self, ctx: HandlerContext, resource: PurgeableResource) -> None: if resource.fail: raise Exception("This resource is set to fail") + ctx.debug("Observed value: %(value)s", value=resource.value) return def create_resource(self, ctx: HandlerContext, resource: PurgeableResource) -> None: diff --git a/tests/test_reference.py b/tests/test_reference.py new file mode 100644 index 00000000..48e09f8f --- /dev/null +++ b/tests/test_reference.py @@ -0,0 +1,54 @@ +""" +Copyright 2025 Inmanta + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +Contact: code@inmanta.com +""" + +from pytest_inmanta.plugin import Project + +from inmanta import const + + +def test_references_resource(project: Project, monkeypatch) -> None: + + project.compile( + """ + import std::testing + metavalue = std::create_environment_reference("METATESTENV") + value = std::create_environment_reference(metavalue) + std::testing::NullResource(agentname="test", name="aaa", value=value) + """ + ) + + project.deploy_resource_v2( + "std::testing::NullResource", + name="aaa", + expected_status=const.ResourceState.failed, + ) + + monkeypatch.setenv("METATESTENV", "TESTENV") + monkeypatch.setenv("TESTENV", "testvalue") + + project.compile( + """ + import std::testing + metavalue = std::create_environment_reference("METATESTENV") + value = std::create_environment_reference(metavalue) + std::testing::NullResource(agentname="test", name="aaa", value=value) + """ + ) + + result = project.deploy_resource_v2("std::testing::NullResource", name="aaa") + assert result.assert_has_logline("Observed value: testvalue")