Skip to content

Commit 9fc8fbb

Browse files
czentgrashkrisk
andcommitted
[native] Add support for env vars providing property values
This allows a user to configure the value of an environment variable to be used as value for a configuration property. For example, the configuration file may contain keys that would be hard coded. The values can now be replaced by environment variables that are read at startup time and the keys don't require hard coding in the configuration file. Co-authored-by: ashkrisk <[email protected]>
1 parent b823353 commit 9fc8fbb

File tree

3 files changed

+123
-46
lines changed

3 files changed

+123
-46
lines changed

presto-docs/src/main/sphinx/presto_cpp/properties.rst

Lines changed: 53 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@ Presto C++ Configuration Properties
44

55
This section describes Presto C++ configuration properties.
66

7-
The following is not a complete list of all configuration properties,
8-
and does not include any connector-specific catalog configuration properties
9-
or session properties.
7+
The following is not a complete list of all configuration properties,
8+
and does not include any connector-specific catalog configuration properties
9+
or session properties.
1010

1111
For information on catalog configuration properties, see :doc:`Connectors </connector/>`.
1212

@@ -20,9 +20,9 @@ For information on Presto C++ session properties, see :doc:`properties-session`.
2020
Coordinator Properties
2121
----------------------
2222

23-
Set the following configuration properties for the Presto coordinator exactly
24-
as they are shown in this code block to enable the Presto coordinator's use of
25-
Presto C++ workers.
23+
Set the following configuration properties for the Presto coordinator exactly
24+
as they are shown in this code block to enable the Presto coordinator's use of
25+
Presto C++ workers.
2626

2727
.. code-block:: none
2828
@@ -32,17 +32,17 @@ Presto C++ workers.
3232
use-alternative-function-signatures=true
3333
experimental.table-writer-merge-operator-enabled=false
3434
35-
These Presto coordinator configuration properties are described here, in
36-
alphabetical order.
35+
These Presto coordinator configuration properties are described here, in
36+
alphabetical order.
3737

3838
``driver.cancel-tasks-with-stuck-operators-threshold-ms``
3939
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
4040
* **Type:** ``string``
4141
* **Default value:** ``240000`` (40 minutes)
4242

43-
Cancels any task when at least one operator has been stuck for at
43+
Cancels any task when at least one operator has been stuck for at
4444
least the time specified by this threshold.
45-
45+
4646
Set this property to ``0`` to disable canceling.
4747

4848
``experimental.table-writer-merge-operator-enabled``
@@ -51,16 +51,16 @@ alphabetical order.
5151
* **Type:** ``boolean``
5252
* **Default value:** ``true``
5353

54-
Merge TableWriter output before sending to TableFinishOperator. This property must be set to
55-
``false``.
54+
Merge TableWriter output before sending to TableFinishOperator. This property must be set to
55+
``false``.
5656

5757
``native-execution-enabled``
5858
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
5959

6060
* **Type:** ``boolean``
6161
* **Default value:** ``false``
6262

63-
This property is required when running Presto C++ workers because of
63+
This property is required when running Presto C++ workers because of
6464
underlying differences in behavior from Java workers.
6565

6666
``optimizer.optimize-hash-generation``
@@ -69,9 +69,9 @@ alphabetical order.
6969
* **Type:** ``boolean``
7070
* **Default value:** ``true``
7171

72-
Set this property to ``false`` when running Presto C++ workers.
73-
Velox does not support optimized hash generation, instead using a HashTable
74-
with adaptive runtime optimizations that does not use extra hash fields.
72+
Set this property to ``false`` when running Presto C++ workers.
73+
Velox does not support optimized hash generation, instead using a HashTable
74+
with adaptive runtime optimizations that does not use extra hash fields.
7575

7676
``regex-library``
7777
^^^^^^^^^^^^^^^^^
@@ -88,17 +88,17 @@ alphabetical order.
8888
* **Type:** ``boolean``
8989
* **Default value:** ``false``
9090

91-
Some aggregation functions use generic intermediate types which are
92-
not compatible with Velox aggregation function intermediate types. One
93-
example function is ``approx_distinct``, whose intermediate type is
94-
``VARBINARY``.
95-
This property provides function signatures for built-in aggregation
91+
Some aggregation functions use generic intermediate types which are
92+
not compatible with Velox aggregation function intermediate types. One
93+
example function is ``approx_distinct``, whose intermediate type is
94+
``VARBINARY``.
95+
This property provides function signatures for built-in aggregation
9696
functions which are compatible with Velox.
9797

9898
Worker Properties
9999
-----------------
100100

101-
The configuration properties of Presto C++ workers are described here, in alphabetical order.
101+
The configuration properties of Presto C++ workers are described here, in alphabetical order.
102102

103103
``async-cache-persistence-interval``
104104
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -302,8 +302,8 @@ The configuration properties of Presto C++ workers are described here, in alphab
302302
Memory Checker Properties
303303
-------------------------
304304

305-
The LinuxMemoryChecker extends from PeriodicMemoryChecker and is used for Linux systems only.
306-
The LinuxMemoryChecker can be enabled by setting the CMake flag ``PRESTO_MEMORY_CHECKER_TYPE=LINUX_MEMORY_CHECKER``.
305+
The LinuxMemoryChecker extends from PeriodicMemoryChecker and is used for Linux systems only.
306+
The LinuxMemoryChecker can be enabled by setting the CMake flag ``PRESTO_MEMORY_CHECKER_TYPE=LINUX_MEMORY_CHECKER``.
307307
The following properties for PeriodicMemoryChecker are as follows:
308308

309309
``system-mem-pushback-enabled``
@@ -322,7 +322,7 @@ server is under low memory pressure.
322322
* **Default value:** ``55``
323323

324324
Specifies the system memory limit that triggers the memory pushback or heap dump if
325-
the server memory usage is beyond this limit. A value of zero means no limit is set.
325+
the server memory usage is beyond this limit. A value of zero means no limit is set.
326326
This only applies if ``system-mem-pushback-enabled`` is ``true``.
327327

328328
``system-mem-shrink-gb``
@@ -333,3 +333,31 @@ This only applies if ``system-mem-pushback-enabled`` is ``true``.
333333

334334
Specifies the amount of memory to shrink when the memory pushback is
335335
triggered. This only applies if ``system-mem-pushback-enabled`` is ``true``.
336+
337+
Environment variables as values for worker properties
338+
-----------------------------------------------------
339+
340+
This section applies to worker configurations in the config.properies file
341+
and catalog property files only.
342+
343+
The value in a key-value par may reference an environment variable by using
344+
a leading `$` followed by enclosing the environment variable name in brackets (`{}`).
345+
346+
```
347+
key=${ENV_VAR_NAME}
348+
```
349+
350+
The environment variable name must match exactly with the defined variable.
351+
352+
This allows a worker to read sensitive data e.g. for access keys from an
353+
environment variable rather than having the actual value hard coded in a comnfiguration
354+
file on disk enhancing the security stance of deployments.
355+
356+
For example, consider the hive connector's `hive.s3.aws-access-key` property.
357+
This is sensitive data and can be stored in an environment variable, for example,
358+
`AWS_S3_ACCESS_KEY` which is set to the actual access key value. Then the following can be
359+
put into the catalog properties to read the value from the environment variable:
360+
361+
```
362+
hive.s3.aws-access-key=${AWS_S3_ACCESS_KEY}
363+
```

presto-native-execution/presto_cpp/main/common/ConfigReader.cpp

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,31 @@
1919
#include "velox/common/config/Config.h"
2020

2121
namespace facebook::presto::util {
22+
23+
namespace {
24+
// Replaces strings of the form "${VAR}"
25+
// with the value of the environment variable "VAR" (if it exists).
26+
// Does nothing if the input doesn't look like "${...}".
27+
std::optional<std::string> extractValueIfEnvironmentVariable(
28+
std::string_view str) {
29+
std::optional<std::string> value{std::nullopt};
30+
if (str.size() >= 3 && str.substr(0, 2) == "${" &&
31+
str[str.size() - 1] == '}') {
32+
auto env_name = std::string(str.substr(2, str.size() - 3));
33+
34+
char* envval = std::getenv(env_name.c_str());
35+
if (envval != nullptr) {
36+
VELOX_CHECK_NE(
37+
strlen(envval),
38+
0,
39+
"The environment variable cannot be an empty string.");
40+
value = std::string(envval);
41+
}
42+
}
43+
return value;
44+
}
45+
} // namespace
46+
2247
std::unordered_map<std::string, std::string> readConfig(
2348
const std::string& filePath) {
2449
// https://teradata.github.io/presto/docs/141t/configuration/configuration.html
@@ -45,7 +70,9 @@ std::unordered_map<std::string, std::string> readConfig(
4570
const auto name = line.substr(0, delimiterPos);
4671
VELOX_CHECK(!name.empty(), "property pair '{}' has empty key", line);
4772
const auto value = line.substr(delimiterPos + 1);
48-
properties.emplace(name, value);
73+
const auto adjustedValue = extractValueIfEnvironmentVariable(value);
74+
adjustedValue.has_value() ? properties.emplace(name, adjustedValue.value())
75+
: properties.emplace(name, value);
4976
}
5077

5178
return properties;

presto-native-execution/presto_cpp/main/common/tests/ConfigTest.cpp

Lines changed: 42 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -33,15 +33,15 @@ class ConfigTest : public testing::Test {
3333
if (tempDirectoryPath == nullptr) {
3434
throw std::logic_error("Cannot open temp directory");
3535
}
36-
configFilePath = tempDirectoryPath;
37-
configFilePath += "/config.properties";
36+
configFilePath_ = tempDirectoryPath;
37+
configFilePath_ += "/config.properties";
3838
}
3939

4040
void writeDefaultConfigFile(bool isMutable) {
41-
auto fileSystem = filesystems::getFileSystem(configFilePath, nullptr);
42-
auto sysConfigFile = fileSystem->openFileForWrite(configFilePath);
41+
auto fileSystem = filesystems::getFileSystem(configFilePath_, nullptr);
42+
auto sysConfigFile = fileSystem->openFileForWrite(configFilePath_);
4343
sysConfigFile->append(
44-
fmt::format("{}={}\n", SystemConfig::kPrestoVersion, prestoVersion));
44+
fmt::format("{}={}\n", SystemConfig::kPrestoVersion, kPrestoVersion_));
4545
sysConfigFile->append(
4646
fmt::format("{}=11kB\n", SystemConfig::kQueryMaxMemoryPerNode));
4747
if (isMutable) {
@@ -52,8 +52,8 @@ class ConfigTest : public testing::Test {
5252
}
5353

5454
void writeConfigFile(const std::string& config) {
55-
auto fileSystem = filesystems::getFileSystem(configFilePath, nullptr);
56-
auto sysConfigFile = fileSystem->openFileForWrite(configFilePath);
55+
auto fileSystem = filesystems::getFileSystem(configFilePath_, nullptr);
56+
auto sysConfigFile = fileSystem->openFileForWrite(configFilePath_);
5757
sysConfigFile->append(config);
5858
sysConfigFile->close();
5959
}
@@ -65,40 +65,41 @@ class ConfigTest : public testing::Test {
6565
std::make_unique<config::ConfigBase>(std::move(properties)));
6666
}
6767

68-
std::string configFilePath;
69-
const std::string prestoVersion{"SystemConfigTest1"};
70-
const std::string prestoVersion2{"SystemConfigTest2"};
68+
std::string configFilePath_;
69+
const std::string kPrestoVersion_{"SystemConfigTest1"};
70+
const std::string kPrestoVersion2_{"SystemConfigTest2"};
7171
};
7272

7373
TEST_F(ConfigTest, defaultSystemConfig) {
7474
setUpConfigFilePath();
7575
writeDefaultConfigFile(false);
7676
auto systemConfig = SystemConfig::instance();
77-
systemConfig->initialize(configFilePath);
77+
systemConfig->initialize(configFilePath_);
7878

7979
ASSERT_FALSE(systemConfig->mutableConfig());
80-
ASSERT_EQ(prestoVersion, systemConfig->prestoVersion());
80+
ASSERT_EQ(kPrestoVersion_, systemConfig->prestoVersion());
8181
ASSERT_EQ(11 << 10, systemConfig->queryMaxMemoryPerNode());
8282
ASSERT_THROW(
8383
systemConfig->setValue(
84-
std::string(SystemConfig::kPrestoVersion), prestoVersion2),
84+
std::string(SystemConfig::kPrestoVersion), kPrestoVersion2_),
8585
VeloxException);
8686
}
8787

8888
TEST_F(ConfigTest, mutableSystemConfig) {
8989
setUpConfigFilePath();
9090
writeDefaultConfigFile(true);
9191
auto systemConfig = SystemConfig::instance();
92-
systemConfig->initialize(configFilePath);
92+
systemConfig->initialize(configFilePath_);
9393

9494
ASSERT_TRUE(systemConfig->mutableConfig());
95-
ASSERT_EQ(prestoVersion, systemConfig->prestoVersion());
95+
ASSERT_EQ(kPrestoVersion_, systemConfig->prestoVersion());
9696
ASSERT_EQ(
97-
prestoVersion,
97+
kPrestoVersion_,
9898
systemConfig
99-
->setValue(std::string(SystemConfig::kPrestoVersion), prestoVersion2)
99+
->setValue(
100+
std::string(SystemConfig::kPrestoVersion), kPrestoVersion2_)
100101
.value());
101-
ASSERT_EQ(prestoVersion2, systemConfig->prestoVersion());
102+
ASSERT_EQ(kPrestoVersion2_, systemConfig->prestoVersion());
102103
ASSERT_EQ(
103104
"11kB",
104105
systemConfig
@@ -220,7 +221,7 @@ TEST_F(ConfigTest, parseValid) {
220221
"key1= value with space\n"
221222
"key2=value=with=key=word\n"
222223
"emptyvaluekey=");
223-
auto configMap = presto::util::readConfig(configFilePath);
224+
auto configMap = presto::util::readConfig(configFilePath_);
224225
ASSERT_EQ(configMap.size(), 6);
225226

226227
std::unordered_map<std::string, std::string> expected{
@@ -240,7 +241,7 @@ TEST_F(ConfigTest, parseInvalid) {
240241
setUpConfigFilePath();
241242
writeConfigFile(fileContent);
242243
VELOX_ASSERT_THROW(
243-
presto::util::readConfig(configFilePath), expectedErrorMsg);
244+
presto::util::readConfig(configFilePath_), expectedErrorMsg);
244245
};
245246
testInvalid(
246247
"noequalsign\n", "No '=' sign found for property pair 'noequalsign'");
@@ -255,4 +256,25 @@ TEST_F(ConfigTest, optionalNodeId) {
255256
EXPECT_EQ(nodeId, config.nodeId());
256257
}
257258

259+
TEST_F(ConfigTest, readConfigEnvVarTest) {
260+
setUpConfigFilePath();
261+
std::string ENV_VAR = "PRESTO_READ_CONFIG_TEST_VAR";
262+
263+
writeConfigFile(
264+
fmt::format("{}={}\n", "plain-text", "plain-text-value") +
265+
fmt::format("{}=${{{}}}\n", "env-var", ENV_VAR) +
266+
fmt::format("{}=${{{}\n", "env-var2", ENV_VAR) +
267+
fmt::format("{}={}}}\n", "env-var3", ENV_VAR) +
268+
fmt::format("{}=${{}}\n", "no-env-var"));
269+
270+
setenv(ENV_VAR.c_str(), "env-var-value", 1);
271+
auto properties = presto::util::readConfig(configFilePath_);
272+
ASSERT_EQ(properties["plain-text"], "plain-text-value");
273+
ASSERT_EQ(properties["env-var"], "env-var-value");
274+
ASSERT_EQ(properties["env-var2"], "${PRESTO_READ_CONFIG_TEST_VAR");
275+
ASSERT_EQ(properties["env-var3"], "PRESTO_READ_CONFIG_TEST_VAR}");
276+
ASSERT_EQ(properties["no-env-var"], "${}");
277+
unsetenv(ENV_VAR.c_str());
278+
}
279+
258280
} // namespace facebook::presto::test

0 commit comments

Comments
 (0)