Skip to content

Commit fbeee81

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 0ee4687 commit fbeee81

File tree

3 files changed

+159
-48
lines changed

3 files changed

+159
-48
lines changed

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

Lines changed: 57 additions & 23 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
@@ -31,17 +31,17 @@ Presto C++ workers.
3131
regex-library=RE2J
3232
use-alternative-function-signatures=true
3333
34-
These Presto coordinator configuration properties are described here, in
35-
alphabetical order.
34+
These Presto coordinator configuration properties are described here, in
35+
alphabetical order.
3636

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

42-
Cancels any task when at least one operator has been stuck for at
42+
Cancels any task when at least one operator has been stuck for at
4343
least the time specified by this threshold.
44-
44+
4545
Set this property to ``0`` to disable canceling.
4646

4747
``native-execution-enabled``
@@ -50,7 +50,7 @@ alphabetical order.
5050
* **Type:** ``boolean``
5151
* **Default value:** ``false``
5252

53-
This property is required when running Presto C++ workers because of
53+
This property is required when running Presto C++ workers because of
5454
underlying differences in behavior from Java workers.
5555

5656
``optimizer.optimize-hash-generation``
@@ -59,9 +59,9 @@ alphabetical order.
5959
* **Type:** ``boolean``
6060
* **Default value:** ``true``
6161

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

6666
``regex-library``
6767
^^^^^^^^^^^^^^^^^
@@ -78,17 +78,17 @@ alphabetical order.
7878
* **Type:** ``boolean``
7979
* **Default value:** ``false``
8080

81-
Some aggregation functions use generic intermediate types which are
82-
not compatible with Velox aggregation function intermediate types. One
83-
example function is ``approx_distinct``, whose intermediate type is
84-
``VARBINARY``.
85-
This property provides function signatures for built-in aggregation
81+
Some aggregation functions use generic intermediate types which are
82+
not compatible with Velox aggregation function intermediate types. One
83+
example function is ``approx_distinct``, whose intermediate type is
84+
``VARBINARY``.
85+
This property provides function signatures for built-in aggregation
8686
functions which are compatible with Velox.
8787

8888
Worker Properties
8989
-----------------
9090

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

9393
``async-cache-persistence-interval``
9494
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -292,8 +292,8 @@ The configuration properties of Presto C++ workers are described here, in alphab
292292
Memory Checker Properties
293293
-------------------------
294294

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

299299
``system-mem-pushback-enabled``
@@ -312,7 +312,7 @@ server is under low memory pressure.
312312
* **Default value:** ``55``
313313

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

318318
``system-mem-shrink-gb``
@@ -323,3 +323,37 @@ This only applies if ``system-mem-pushback-enabled`` is ``true``.
323323

324324
Specifies the amount of memory to shrink when the memory pushback is
325325
triggered. This only applies if ``system-mem-pushback-enabled`` is ``true``.
326+
327+
Environment Variables As Values For Worker Properties
328+
-----------------------------------------------------
329+
330+
This section applies to worker configurations in the config.properies file
331+
and catalog property files only.
332+
333+
The value in a key-value par may reference an environment variable by using
334+
a leading `$` followed by enclosing the environment variable name in brackets (`{}`).
335+
336+
```
337+
key=${ENV_VAR_NAME}
338+
```
339+
340+
The environment variable name must match exactly with the defined variable.
341+
342+
This allows a worker to read sensitive data e.g. for access keys from an
343+
environment variable rather than having the actual value hard coded in a configuration
344+
file on disk enhancing the security of deployments.
345+
346+
For example, consider the hive connector's `hive.s3.aws-access-key` property.
347+
This is sensitive data and can be stored in an environment variable, for example,
348+
`AWS_S3_ACCESS_KEY` which is set to the actual access key value.
349+
350+
One mechanism is to create a preload library that is injected at the time
351+
presto_server is started. It decrypts encrypted secrets and sets environment
352+
variables specific to the presto_server process. These can then be referenced
353+
in the properies.
354+
355+
For example, once decrypted the preloaded library sets the AWS_S3_ACCESS_KEY
356+
environment variable which then can be accessed by providing it in the catlog properties:
357+
```
358+
hive.s3.aws-access-key=${AWS_S3_ACCESS_KEY}
359+
```

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

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,27 @@
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+
void extractValueIfEnvironmentVariable(std::string& value) {
28+
if (value.size() > 3 && value.substr(0, 2) == "${" && value.back() == '}') {
29+
auto envName = value.substr(2, value.size() - 3);
30+
31+
const char* envVal = std::getenv(envName.c_str());
32+
if (envVal != nullptr) {
33+
if (strlen(envVal) == 0) {
34+
LOG(WARNING) << fmt::format(
35+
"Config environment variable {} was empty.", envVal);
36+
}
37+
value = std::string(envVal);
38+
}
39+
}
40+
}
41+
} // namespace
42+
2243
std::unordered_map<std::string, std::string> readConfig(
2344
const std::string& filePath) {
2445
// https://teradata.github.io/presto/docs/141t/configuration/configuration.html
@@ -44,7 +65,8 @@ std::unordered_map<std::string, std::string> readConfig(
4465
line);
4566
const auto name = line.substr(0, delimiterPos);
4667
VELOX_CHECK(!name.empty(), "property pair '{}' has empty key", line);
47-
const auto value = line.substr(delimiterPos + 1);
68+
auto value = line.substr(delimiterPos + 1);
69+
extractValueIfEnvironmentVariable(value);
4870
properties.emplace(name, value);
4971
}
5072

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

Lines changed: 79 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -25,23 +25,36 @@ using namespace velox;
2525

2626
class ConfigTest : public testing::Test {
2727
protected:
28-
void setUpConfigFilePath() {
28+
void SetUp() override {
2929
velox::filesystems::registerLocalFileSystem();
30+
setUpConfigFilePath();
31+
}
32+
33+
void TearDown() override {
34+
cleanupConfigFilePath();
35+
}
3036

37+
void setUpConfigFilePath() {
3138
char path[] = "/tmp/velox_system_config_test_XXXXXX";
3239
const char* tempDirectoryPath = mkdtemp(path);
3340
if (tempDirectoryPath == nullptr) {
3441
throw std::logic_error("Cannot open temp directory");
3542
}
36-
configFilePath = tempDirectoryPath;
37-
configFilePath += "/config.properties";
43+
configFilePath_ = tempDirectoryPath;
44+
configFilePath_ += "/config.properties";
45+
}
46+
47+
void cleanupConfigFilePath() {
48+
auto fileSystem = filesystems::getFileSystem(configFilePath_, nullptr);
49+
auto dirPath = std::filesystem::path(configFilePath_).parent_path();
50+
fileSystem->rmdir(dirPath.string());
3851
}
3952

4053
void writeDefaultConfigFile(bool isMutable) {
41-
auto fileSystem = filesystems::getFileSystem(configFilePath, nullptr);
42-
auto sysConfigFile = fileSystem->openFileForWrite(configFilePath);
54+
auto fileSystem = filesystems::getFileSystem(configFilePath_, nullptr);
55+
auto sysConfigFile = fileSystem->openFileForWrite(configFilePath_);
4356
sysConfigFile->append(
44-
fmt::format("{}={}\n", SystemConfig::kPrestoVersion, prestoVersion));
57+
fmt::format("{}={}\n", SystemConfig::kPrestoVersion, kPrestoVersion_));
4558
sysConfigFile->append(
4659
fmt::format("{}=11kB\n", SystemConfig::kQueryMaxMemoryPerNode));
4760
if (isMutable) {
@@ -52,8 +65,8 @@ class ConfigTest : public testing::Test {
5265
}
5366

5467
void writeConfigFile(const std::string& config) {
55-
auto fileSystem = filesystems::getFileSystem(configFilePath, nullptr);
56-
auto sysConfigFile = fileSystem->openFileForWrite(configFilePath);
68+
auto fileSystem = filesystems::getFileSystem(configFilePath_, nullptr);
69+
auto sysConfigFile = fileSystem->openFileForWrite(configFilePath_);
5770
sysConfigFile->append(config);
5871
sysConfigFile->close();
5972
}
@@ -65,40 +78,41 @@ class ConfigTest : public testing::Test {
6578
std::make_unique<config::ConfigBase>(std::move(properties)));
6679
}
6780

68-
std::string configFilePath;
69-
const std::string prestoVersion{"SystemConfigTest1"};
70-
const std::string prestoVersion2{"SystemConfigTest2"};
81+
std::string configFilePath_;
82+
const std::string_view kPrestoVersion_{"SystemConfigTest1"};
83+
const std::string_view kPrestoVersion2_{"SystemConfigTest2"};
7184
};
7285

7386
TEST_F(ConfigTest, defaultSystemConfig) {
74-
setUpConfigFilePath();
7587
writeDefaultConfigFile(false);
7688
auto systemConfig = SystemConfig::instance();
77-
systemConfig->initialize(configFilePath);
89+
systemConfig->initialize(configFilePath_);
7890

7991
ASSERT_FALSE(systemConfig->mutableConfig());
80-
ASSERT_EQ(prestoVersion, systemConfig->prestoVersion());
92+
ASSERT_EQ(kPrestoVersion_, systemConfig->prestoVersion());
8193
ASSERT_EQ(11 << 10, systemConfig->queryMaxMemoryPerNode());
8294
ASSERT_THROW(
8395
systemConfig->setValue(
84-
std::string(SystemConfig::kPrestoVersion), prestoVersion2),
96+
std::string(SystemConfig::kPrestoVersion),
97+
std::string(kPrestoVersion2_)),
8598
VeloxException);
8699
}
87100

88101
TEST_F(ConfigTest, mutableSystemConfig) {
89-
setUpConfigFilePath();
90102
writeDefaultConfigFile(true);
91103
auto systemConfig = SystemConfig::instance();
92-
systemConfig->initialize(configFilePath);
104+
systemConfig->initialize(configFilePath_);
93105

94106
ASSERT_TRUE(systemConfig->mutableConfig());
95-
ASSERT_EQ(prestoVersion, systemConfig->prestoVersion());
107+
ASSERT_EQ(kPrestoVersion_, systemConfig->prestoVersion());
96108
ASSERT_EQ(
97-
prestoVersion,
109+
kPrestoVersion_,
98110
systemConfig
99-
->setValue(std::string(SystemConfig::kPrestoVersion), prestoVersion2)
111+
->setValue(
112+
std::string(SystemConfig::kPrestoVersion),
113+
std::string(kPrestoVersion2_))
100114
.value());
101-
ASSERT_EQ(prestoVersion2, systemConfig->prestoVersion());
115+
ASSERT_EQ(kPrestoVersion2_, systemConfig->prestoVersion());
102116
ASSERT_EQ(
103117
"11kB",
104118
systemConfig
@@ -206,7 +220,6 @@ TEST_F(ConfigTest, remoteFunctionServer) {
206220
}
207221

208222
TEST_F(ConfigTest, parseValid) {
209-
setUpConfigFilePath();
210223
writeConfigFile(
211224
"#comment\n"
212225
"#a comment with space\n"
@@ -220,7 +233,7 @@ TEST_F(ConfigTest, parseValid) {
220233
"key1= value with space\n"
221234
"key2=value=with=key=word\n"
222235
"emptyvaluekey=");
223-
auto configMap = presto::util::readConfig(configFilePath);
236+
auto configMap = presto::util::readConfig(configFilePath_);
224237
ASSERT_EQ(configMap.size(), 6);
225238

226239
std::unordered_map<std::string, std::string> expected{
@@ -237,10 +250,11 @@ TEST_F(ConfigTest, parseInvalid) {
237250
auto testInvalid = [this](
238251
const std::string& fileContent,
239252
const std::string& expectedErrorMsg) {
253+
cleanupConfigFilePath();
240254
setUpConfigFilePath();
241255
writeConfigFile(fileContent);
242256
VELOX_ASSERT_THROW(
243-
presto::util::readConfig(configFilePath), expectedErrorMsg);
257+
presto::util::readConfig(configFilePath_), expectedErrorMsg);
244258
};
245259
testInvalid(
246260
"noequalsign\n", "No '=' sign found for property pair 'noequalsign'");
@@ -255,4 +269,45 @@ TEST_F(ConfigTest, optionalNodeId) {
255269
EXPECT_EQ(nodeId, config.nodeId());
256270
}
257271

272+
TEST_F(ConfigTest, readConfigEnvVarTest) {
273+
const std::string kEnvVarName = "PRESTO_READ_CONFIG_TEST_VAR";
274+
const std::string kEmptyEnvVarName = "PRESTO_READ_CONFIG_TEST_EMPTY_VAR";
275+
276+
const std::string kPlainTextKey = "plain-text";
277+
const std::string kPlainTextValue = "plain-text-value";
278+
279+
const std::string kEnvVarKey = "env-var";
280+
const std::string kEnvVarValue = "env-var-value";
281+
282+
// Keys to test invalid environment variable values.
283+
const std::string kEnvVarKey2 = "env-var2";
284+
const std::string kEnvVarKey3 = "env-var3";
285+
const std::string kNoEnvVarKey = "no-env-var";
286+
const std::string kEmptyEnvVarKey = "empty-env-var";
287+
288+
writeConfigFile(
289+
fmt::format("{}={}\n", kPlainTextKey, kPlainTextValue) +
290+
fmt::format("{}=${{{}}}\n", kEnvVarKey, kEnvVarName) +
291+
fmt::format("{}=${{{}\n", kEnvVarKey2, kEnvVarName) +
292+
fmt::format("{}={}}}\n", kEnvVarKey3, kEnvVarName) +
293+
fmt::format("{}=${{}}\n", kNoEnvVarKey) +
294+
fmt::format("{}=${{{}}}\n", kEmptyEnvVarKey, kEmptyEnvVarName));
295+
296+
setenv(kEnvVarName.c_str(), kEnvVarValue.c_str(), 1);
297+
setenv(kEmptyEnvVarName.c_str(), "", 1);
298+
299+
auto properties = presto::util::readConfig(configFilePath_);
300+
std::unordered_map<std::string, std::string> expected{
301+
{kPlainTextKey, kPlainTextValue},
302+
{kEnvVarKey, kEnvVarValue},
303+
{kEnvVarKey2, "${PRESTO_READ_CONFIG_TEST_VAR"},
304+
{kEnvVarKey3, "PRESTO_READ_CONFIG_TEST_VAR}"},
305+
{kNoEnvVarKey, "${}"},
306+
{kEmptyEnvVarKey, ""}};
307+
ASSERT_EQ(properties, expected);
308+
309+
unsetenv(kEnvVarName.c_str());
310+
unsetenv(kEmptyEnvVarName.c_str());
311+
}
312+
258313
} // namespace facebook::presto::test

0 commit comments

Comments
 (0)