Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 2 additions & 26 deletions src/libecflow_light/ecflow/light/Configuration.cc
Original file line number Diff line number Diff line change
Expand Up @@ -38,30 +38,6 @@ std::ostream& operator<<(std::ostream& os, const ClientCfg& cfg) {
return os;
}

namespace implementation_detail {

std::string replace_env_var(const std::string& value, const Environment& environment) {
static std::regex regex(R"(\$ENV\{([^}]*)\})");
std::smatch match;
if (bool found = std::regex_match(value, match, regex); found) {
std::string name = match[1];
if (std::optional<Variable> variable = environment.get_optional(name); variable) {
// Attempt to retrieve from 'cached' Environment first
return variable->value;
}
else if (std::optional<Variable> variable = implementation_detail::Environment0::get_variable(name); variable) {
// Then, attempt to retrieve from actual environment variables
return variable->value;
} else {
Log::warning() << Message("Environment variable '", name, "' not found. Replacement not possible...").str()
<< std::endl;
}
}
return value;
}

} // namespace implementation_detail

Configuration Configuration::make_cfg() {
Configuration cfg{};

Expand Down Expand Up @@ -108,8 +84,8 @@ Configuration Configuration::make_cfg() {
std::string version = get("version", "1.0");

// Replace environment variables
host = implementation_detail::replace_env_var(host, environment);
port = implementation_detail::replace_env_var(port, environment);
host = replace_env_var(host, environment);
port = replace_env_var(port, environment);

cfg.clients.push_back(ClientCfg::make_cfg(kind, protocol, host, port, version));

Expand Down
30 changes: 30 additions & 0 deletions src/libecflow_light/ecflow/light/Environment.cc
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,33 @@
*/

#include "ecflow/light/Environment.h"

#include <regex>

#include "ecflow/light/Log.h"

namespace ecflow::light {

std::string replace_env_var(const std::string& parameter, const Environment& environment) {
static std::regex regex(R"(\$ENV\{([^}]*)\})");
std::smatch match;
if (bool found = std::regex_match(parameter, match, regex); found) {
std::string name = match[1];

// Retrieve the variable from the 'cached' Environment
if (std::optional<Variable> variable = environment.get_optional(name); variable) {
return variable->value;
}

// Retrieve the variable from the 'OS' Environment
if (std::optional<Variable> variable = implementation_detail::Environment0::get_variable(name); variable) {
return variable->value;
}

Log::warning() << Message("Environment variable '", name, "' not found. Replacement not possible...").str()
<< std::endl;
}
return parameter;
}

} // namespace ecflow::light
14 changes: 14 additions & 0 deletions src/libecflow_light/ecflow/light/Environment.h
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,20 @@ class Environment {
dict_t environment_;
};

/**
* Replace occurrences of environment variables in the given 'parameter' string,
* using the provided 'environment' for variable lookup.
*
* The expected placeholder for environment variables in the 'parameter' string is:
* $ENV{VARIABLE_NAME}
*
* When the placeholder is found, the corresponding variable is retrieved, either from the 'environment' itself
* or from the OS environment, and its value replaces the corresponding placeholder in the 'parameter' string.
*
* If the variable is not found, a warning is logged, and the original 'parameter' string is returned unchanged.
*/
std::string replace_env_var(const std::string& parameter, const Environment& environment);

} // namespace ecflow::light

#endif
1 change: 1 addition & 0 deletions src/libecflow_light/ecflow/light/Log.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#define ECFLOW_LIGHT_LOG_H

#include <algorithm>
#include <cstdint>
#include <iostream>
#include <sstream>
#include <string>
Expand Down
25 changes: 25 additions & 0 deletions tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -57,3 +57,28 @@ ecbuild_add_test(
)

target_clangformat(TARGET ${TARGET} CONDITION HAVE_TESTS)

# ==============================================================================
# Environment Test

set(TARGET ecflow_light_environment_test)

set(${TARGET}_srcs
# SOURCES
TestEnvironment.cc
)

ecbuild_add_test(
TARGET ${TARGET}
SOURCES
${${TARGET}_srcs}
LIBS
ecflow_light
eckit
CXXFLAGS
${TEST_CXXFLAGS}
ENVIRONMENT
"ECF_SOME_VARIABLE=1500"
)

target_clangformat(TARGET ${TARGET} CONDITION HAVE_TESTS)
72 changes: 72 additions & 0 deletions tests/TestEnvironment.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
/*
* (C) Copyright 2023- ECMWF.
*
* This software is licensed under the terms of the Apache Licence version 2.0
* which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
* In applying this licence, ECMWF does not waive the privileges and immunities
* granted to it by virtue of its status as an intergovernmental organisation
* nor does it submit to any jurisdiction.
*/

#include <eckit/testing/Test.h>

#include "ecflow/light/ClientAPI.h"

namespace ecfl = ecflow::light;


namespace ecflow::light::testing {

CASE("test_environment__can_handle_cached_environment_variables") {
Environment environment = Environment::an_environment()
.with("ECF_RID", "12345")
.with("ECF_NAME", "/path/to/task")
.with("ECF_PASS", "custom_password");

auto rid = environment.get_optional("ECF_RID");
EXPECT(rid && rid->value == "12345");
auto name = environment.get_optional("ECF_NAME");
EXPECT(name && name->value == "/path/to/task");
auto pass = environment.get_optional("ECF_PASS");
EXPECT(pass && pass->value == "custom_password");
}

CASE("test_environment__can_handle_non_cached_environment_variables") {
Environment environment = Environment::an_environment()
.with("ECF_RID", "12345")
.with("ECF_NAME", "/path/to/task")
.with("ECF_PASS", "custom_password");

auto nonexistent = environment.get_optional("__NONEXISTENT__");
EXPECT(!nonexistent);
}

CASE("test_environment__can_replace_environment_variables") {
Environment environment = Environment::an_environment()
.with("ECF_RID", "12345")
.with("ECF_NAME", "/path/to/task")
.with("ECF_PASS", "custom_password");

{
// No replacement actually necessary
auto result = replace_env_var("somevalue", environment);
EXPECT(result == "somevalue");
}
{
// Replace variable, based on 'cached' environment
auto result = replace_env_var("$ENV{ECF_NAME}", environment);
EXPECT(result == "/path/to/task");
}
{
// Replace variable, based on 'real' environment
// The following 'SOME_VARIABLE' is set on the environment by CMake
auto result = replace_env_var("$ENV{ECF_SOME_VARIABLE}", environment);
EXPECT(result == "1500");
}
}

} // namespace ecflow::light::testing

int main(int argc, char** argv) {
return eckit::testing::run_tests(argc, argv);
}
Loading