diff --git a/README.md b/README.md
index e86fcd5..d1d069a 100644
--- a/README.md
+++ b/README.md
@@ -141,6 +141,7 @@ What is working:
- Calling RPCs (unary + streaming)
- Input and output of all protocol buffer types
- Security: SSL is supported
+- Saveing parameter in a config file (see below: Working with config Files)
Some notable things which are not yet working:
@@ -162,3 +163,50 @@ Be sure to search issues first to avoid duplicate entries.
Please have a look at [CONTRIBUTE.md](CONTRIBUTING.md) for general information about contributing.
Some more technical documentation can be found here: [Technical Documentation for Developers](doc/Developer.md).
+
+## Working with _config_ files
+gWhisper supports config fies to save values for gWhisper parameter.
+When provideing a setting for a parameter in the config file as well as in the cli command, gWhisper will use the parameter provided via cli.
+
+Example:
+Setting the timeout for rpcs via the config `"RpcTimeoutInMs":"500"` and via the cli `--connectTimeoutMs=100`, gWhisper will use the latter.
+
+
+### Structure
+Use the following structure, if you want to create a config file:
+```sh
+{
+ "configParameter":
+ {
+ "Ssl":null,
+ "SslSettings":
+ {
+ "ServerCertFile":"PathToFile",
+ "ClientCertFile":""PathToFile"",
+ "ClientKeyFile":""PathToFile""
+ },
+ "DisableCache":null,
+ "RpcTimeoutInMs":"TimeInMs",
+ "ConnectTimeoutBlubb":"TimeInMs"
+ }
+}
+```
+
+### Set parameter
+| Parameter | Settings |
+| ------ | ------ |
+| Ssl | "Yes" (string) : Enable a secure connection to the server
null: Disable secure connection |
+| SslSettings | "AbsolutePath" (string): find path with pwd
null: |
+| DisableCache | "Yes" (string) : Enable a secure connection to the server
null: Disable secure connection |
+| RpcTimeoutInMs | "TimeInMs" (string):
null: |
+| ConnectTimeout | "TimeInMs" (string):
null:|
+
+### Where to save the config file
+gWhisper uses config file at /home/usr/.cache/gWhisperConfig.json` as default.
+To the default, name your file `gWhisperConfig` and place it at `gWhisperConfig`
+
+
+
+Custom location
+gWhisper supports config files at locations other than the default location.
+To use custom path to the config file set the paramter `--configFile=` when executing gwhisper.
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index a748fe3..f5b7b0c 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -20,6 +20,7 @@ add_subdirectory(gwhisper)
add_subdirectory(version)
add_subdirectory(utils)
add_subdirectory(libLocalDescriptorCache)
+add_subdirectory(libGwhisperSettings)
get_filename_component(BIN_DIR_ABS ${CMAKE_BINARY_DIR}/. DIRECTORY)
get_filename_component(SRC_DIR_ABS ${CMAKE_SOURCE_DIR}/. DIRECTORY)
diff --git a/src/gwhisper/CMakeLists.txt b/src/gwhisper/CMakeLists.txt
index c0812a4..e151f45 100644
--- a/src/gwhisper/CMakeLists.txt
+++ b/src/gwhisper/CMakeLists.txt
@@ -38,6 +38,7 @@ target_link_libraries ( ${TARGET_NAME}
reflection
version
generateHelpString
+ libGwhisperSettings
)
set_target_properties(
diff --git a/src/gwhisper/gwhisper.cpp b/src/gwhisper/gwhisper.cpp
index a8df901..78d71c2 100644
--- a/src/gwhisper/gwhisper.cpp
+++ b/src/gwhisper/gwhisper.cpp
@@ -20,6 +20,7 @@
#include
#include
#include // generated during build
+#include "GwhisperSettings.hpp"
using namespace ArgParse;
@@ -56,6 +57,7 @@ int main(int argc, char **argv)
std::string args = getArgsAsString(argc, argv);
ParsedElement parseTree;
ParseRc rc = grammarRoot->parse(args.c_str(), parseTree);
+ gWhisperSettings paramProxy(parseTree);
if (parseTree.findFirstChild("DotExport") != "")
{
@@ -63,7 +65,7 @@ int main(int argc, char **argv)
return 0;
}
- if (parseTree.findFirstChild("Complete") != "")
+ if (paramProxy.lookUpSetting("Complete", parseTree) != "")
{
bool completeDebug = (parseTree.findFirstChild("CompleteDebug") != "");
if (parseTree.findFirstChild("fish") != "")
@@ -113,7 +115,7 @@ int main(int argc, char **argv)
{
//printf("\nchoice:\n%s", candidate->getDebugString().c_str());
printf("\n '%s'", candidate->getMatchedString().c_str());
- }
+ }
std::cout << std::endl;
}
diff --git a/src/libCli/CMakeLists.txt b/src/libCli/CMakeLists.txt
index 3b531d8..ef6bb47 100644
--- a/src/libCli/CMakeLists.txt
+++ b/src/libCli/CMakeLists.txt
@@ -12,6 +12,9 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+include(FetchContent)
+FetchContent_Declare(json URL https://github.com/nlohmann/json/releases/download/v3.10.5/json.tar.xz)
+FetchContent_MakeAvailable(json)
set(TARGET_NAME "cli")
set(TARGET_SRC
@@ -34,12 +37,14 @@ target_link_libraries ( ${TARGET_NAME}
PRIVATE
reflection
protoDoc
+ nlohmann_json::nlohmann_json
)
target_link_libraries ( ${TARGET_NAME}
PUBLIC
gwhisperUtils
DescDbProxy
ArgParse
+ libGwhisperSettings
)
target_include_directories(${TARGET_NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
diff --git a/src/libCli/Call.cpp b/src/libCli/Call.cpp
index e5dbef8..fe3a572 100644
--- a/src/libCli/Call.cpp
+++ b/src/libCli/Call.cpp
@@ -23,6 +23,7 @@
#include
#include
#include
+#include "GwhisperSettings.hpp"
// for detecting if we are writing stdout to terminal or to pipe/file
#include
@@ -32,6 +33,7 @@
#include
using namespace ArgParse;
+using json = nlohmann::json;
namespace cli
{
@@ -42,9 +44,9 @@ namespace cli
/// created.
/// @param parseTree CLI argument parse tree, which will be used to detemrine
/// which OputputFormatter to create.
- std::unique_ptr createMessageFormatter(ParsedElement &parseTree)
+ std::unique_ptr createMessageFormatter(ParsedElement &parseTree, gWhisperSettings f_settingProxy)
{
- if(parseTree.findFirstChild("JsonOutput") != "")
+ if(f_settingProxy.lookUpSetting("JsonOutput", parseTree) != "")
{
return std::make_unique();
}
@@ -59,13 +61,14 @@ namespace cli
auto humanFormatter = std::make_unique();
// disable colored output if explicitly specified:
- if (parseTree.findFirstChild("NoColor") != "")
+ //if (parseTree.findFirstChild("NoColor") != "")
+ if (f_settingProxy.lookUpSetting("NoColor", parseTree) != "")
{
humanFormatter->clearColorMap();
}
// disable map output as key => value if explicitly specified:
- if (parseTree.findFirstChild("NoSimpleMapOutput") != "")
+ if (f_settingProxy.lookUpSetting("NoSimpleMapOutput", parseTree) != "")
{
humanFormatter->disableSimpleMapOutput();
}
@@ -73,7 +76,8 @@ namespace cli
// automatically disable colored output, when outputting to something
// else than a terminal (pipes, files, etc.), except we explicitly
// request color mode:
- if ((not isatty(fileno(stdout))) and (parseTree.findFirstChild("Color") == ""))
+ //if ((not isatty(fileno(stdout))) and (parseTree.findFirstChild("Color") == ""))
+ if ((not isatty(fileno(stdout))) and (f_settingProxy.lookUpSetting("Color", parseTree) == ""))
{
humanFormatter->clearColorMap();
}
@@ -113,6 +117,7 @@ namespace cli
int call(ParsedElement &parseTree)
{
+ gWhisperSettings settingProxy(parseTree);
std::string serviceName = parseTree.findFirstChild("Service");
std::string methodName = parseTree.findFirstChild("Method");
bool argsExist;
@@ -121,7 +126,8 @@ namespace cli
std::shared_ptr channel = ConnectionManager::getInstance().getChannel(serverAddress, parseTree);
- if (not waitForChannelConnected(channel, getConnectTimeoutMs(&parseTree)))
+ std::string connectTimeoutStr = settingProxy.lookUpSetting("ConnectTimeout", parseTree);
+ if (not waitForChannelConnected(channel, getConnectTimeoutMs(connectTimeoutStr)))
{
std::cerr << "Error: channel connection attempt timed out" << std::endl;
return -1;
@@ -152,8 +158,7 @@ namespace cli
std::optional> deadline;
std::chrono::time_point defaultDeadline = std::chrono::system_clock::now() + std::chrono::milliseconds(30000);
-
- bool setTimeout = (parseTree.findFirstChild("rpcTimeout") != "");
+ bool setTimeout = (settingProxy.lookUpSetting("RpcTimeoutInMs", parseTree) != "");
if(!setTimeout)
{
@@ -170,16 +175,20 @@ namespace cli
if(setTimeout)
{
- if (parseTree.findFirstChild("manualInfiniteTimeout") != ""){
+ std::string timeoutTime = settingProxy.lookUpSetting("RpcTimeoutInMs", parseTree);
+ if (settingProxy.lookUpSetting("RpcTimeoutInMs", parseTree) == "None")
+ {
+ // set infinite deadline for unary RPCs
deadline = std::nullopt;
}
else
{
- std::string customTimeout = parseTree.findFirstChild("rpcTimeoutInMs");
+ std::string customTimeout = settingProxy.lookUpSetting("RpcTimeoutInMs", parseTree); //check if none or number string
+
unsigned long customTimeoutMs;
try
{
- customTimeoutMs = std::stoul(customTimeout, nullptr, 0);
+ customTimeoutMs = std::stoul(customTimeout, nullptr, 0);
}
catch(std::exception& e)
{
@@ -192,7 +201,7 @@ namespace cli
grpc::testing::CliCall call(channel, methodStr, clientMetadata, deadline);
- auto messageFormatter = createMessageFormatter(parseTree);
+ auto messageFormatter = createMessageFormatter(parseTree, settingProxy);
auto messageParser = createMessageParser(parseTree);
// NOTE: need to create and hold message factory here, as it holds
@@ -217,7 +226,7 @@ namespace cli
// Write all request messages (multiple in case of request stream)
for (auto & message : requestMessages)
{
- if (parseTree.findFirstChild("PrintParsedMessage") != "")
+ if (settingProxy.lookUpSetting("PrintParsedMessage", parseTree) != "")
{
std::cout << "Request message:" << std::endl
<< messageFormatter->messageToString(*message, method->input_type()) << std::endl;
diff --git a/src/libCli/ConnectionManager.cpp b/src/libCli/ConnectionManager.cpp
index 74b8732..62158cf 100644
--- a/src/libCli/ConnectionManager.cpp
+++ b/src/libCli/ConnectionManager.cpp
@@ -22,6 +22,7 @@
#include
#include
#include
+#include "GwhisperSettings.hpp"
namespace cli
{
@@ -122,17 +123,13 @@ namespace cli
ConnList connection;
std::shared_ptr creds;
std::shared_ptr channelCreds;
+ gWhisperSettings settingProxy(f_parseTree); // Todo: Replacef_parseTree with config object in signature of Connectionmanager
- if (f_parseTree.findFirstChild("ssl") != "")
+ if (settingProxy.lookUpSetting("Ssl", f_parseTree) != "")
{
- // if --ssl set is set, check if user provides keys/ certs
- bool clientCertOption = (f_parseTree.findFirstChild("OptionClientCert") != "");
- bool clientKeyOption = (f_parseTree.findFirstChild("OptionClientKey") != "");
- bool serverCertOption = (f_parseTree.findFirstChild("OptionServerCert") != "");
-
- std::string sslClientCertPath = f_parseTree.findFirstChild("FileClientCert");
- std::string sslClientKeyPath = f_parseTree.findFirstChild("FileClientKey");
- std::string sslServerCertPath = f_parseTree.findFirstChild("FileServerCert");
+ std::string sslClientCertPath = settingProxy.lookUpSetting("ClientCertFile", f_parseTree);
+ std::string sslClientKeyPath = settingProxy.lookUpSetting("ClientKeyFile", f_parseTree);
+ std::string sslServerCertPath = settingProxy.lookUpSetting("ServerCertFile", f_parseTree);
// debugString = "CREATE SECURE CAHNNEL WITH USER-PROVIDED CREDENTIALS";
channelCreds = generateSSLCredentials(sslClientCertPath, sslClientKeyPath, sslServerCertPath);
@@ -145,7 +142,6 @@ namespace cli
}
bool disableCache = (f_parseTree.findFirstChild("DisableCache") != "");
- // Timeout as chrono duration
connection.descDbProxy = std::make_shared(disableCache, f_serverAddress, connection.channel, f_parseTree);
connection.descPool = std::make_shared(connection.descDbProxy.get());
diff --git a/src/libCli/GrammarConstruction.cpp b/src/libCli/GrammarConstruction.cpp
index afbf9b5..3cf55f1 100644
--- a/src/libCli/GrammarConstruction.cpp
+++ b/src/libCli/GrammarConstruction.cpp
@@ -455,21 +455,21 @@ namespace cli
GrammarElement *optionsalt = f_grammarPool.createElement();
optionsalt->addChild(f_grammarPool.createElement("-h", "Help"));
optionsalt->addChild(f_grammarPool.createElement("--help", "Help"));
- optionsalt->addChild(f_grammarPool.createElement("--ssl", "ssl"));
+ optionsalt->addChild(f_grammarPool.createElement("--ssl", "Ssl"));
GrammarElement *clientCert = f_grammarPool.createElement();
clientCert->addChild(f_grammarPool.createElement("--clientCert=", "OptionClientCert"));
- clientCert->addChild(f_grammarPool.createElement(" %", '%', "FileClientCert"));
+ clientCert->addChild(f_grammarPool.createElement(" %", '%', "ClientCertFile"));
optionsalt->addChild(clientCert);
GrammarElement *clientKey = f_grammarPool.createElement();
clientKey->addChild(f_grammarPool.createElement("--clientKey=", "OptionClientKey"));
- clientKey->addChild(f_grammarPool.createElement(" %", '%', "FileClientKey"));
+ clientKey->addChild(f_grammarPool.createElement(" %", '%', "ClientKeyFile"));
optionsalt->addChild(clientKey);
GrammarElement *serverCert = f_grammarPool.createElement();
serverCert->addChild(f_grammarPool.createElement("--serverCert=", "OptionServerCert"));
- serverCert->addChild(f_grammarPool.createElement(" %", '%', "FileServerCert"));
+ serverCert->addChild(f_grammarPool.createElement(" %", '%', "ServerCertFile"));
optionsalt->addChild(serverCert);
GrammarElement *completeOption = f_grammarPool.createElement();
@@ -485,6 +485,11 @@ namespace cli
completeDialectChoice->addChild(f_grammarPool.createElement("fish", "fish"));
optionsalt->addChild(completeOption);
+ GrammarElement *config = f_grammarPool.createElement();
+ config->addChild(f_grammarPool.createElement("--configFile=", "ConfigFile"));
+ config->addChild(f_grammarPool.createElement(" %", '%', "ConfigFilePath"));
+ optionsalt->addChild(config);
+
//completeOption->addChild(f_grammarPool.createElement("--complete", "Complete"));
optionsalt->addChild(f_grammarPool.createElement("--debugComplete", "CompleteDebug"));
optionsalt->addChild(f_grammarPool.createElement("--disableCache", "DisableCache"));
@@ -501,19 +506,19 @@ namespace cli
optionsalt->addChild(f_grammarPool.createElement("--noSimpleMapOutput", "NoSimpleMapOutput"));
GrammarElement *timeout = f_grammarPool.createElement();
- timeout->addChild(f_grammarPool.createElement("--rpcTimeoutMilliseconds", "rpcTimeout"));
+ timeout->addChild(f_grammarPool.createElement("--rpcTimeoutMilliseconds")); //RPCTimeout
timeout->addChild(f_grammarPool.createElement("="));
- GrammarElement *timeoutTime = f_grammarPool.createElement();
+ GrammarElement *timeoutTime = f_grammarPool.createElement("RpcTimeoutInMs");
timeout->addChild(timeoutTime);
- timeoutTime->addChild(f_grammarPool.createElement("[0-9]+", "rpcTimeoutInMs"));
+ timeoutTime->addChild(f_grammarPool.createElement("[0-9]+")); // RpcTimeoutInMs
GrammarElement *manualInfiniteTimeout = f_grammarPool.createElement();
timeoutTime->addChild(manualInfiniteTimeout);
- manualInfiniteTimeout->addChild(f_grammarPool.createElement("None", "manualInfiniteTimeout"));
+ manualInfiniteTimeout->addChild(f_grammarPool.createElement("None")); // manualInfiniteTimeout
optionsalt->addChild(timeout);
GrammarElement *timeoutOption = f_grammarPool.createElement();
timeoutOption->addChild(f_grammarPool.createElement("--connectTimeoutMilliseconds="));
- timeoutOption->addChild(f_grammarPool.createElement("[0-9]+", "connectTimeout"));
+ timeoutOption->addChild(f_grammarPool.createElement("[0-9]+", "ConnectTimeout"));
optionsalt->addChild(timeoutOption);
optionsalt->addChild(customOutputFormat);
// FIXME FIXME FIXME: we cannot distinguish between --complete and --completeDebug.. this is a problem for arguments too, as we cannot guarantee, that we do not have an argument starting with the name of an other argument.
diff --git a/src/libCli/cliUtils.cpp b/src/libCli/cliUtils.cpp
index b204c7b..e1ea4db 100644
--- a/src/libCli/cliUtils.cpp
+++ b/src/libCli/cliUtils.cpp
@@ -9,15 +9,14 @@ namespace cli
return result;
}
- uint32_t getConnectTimeoutMs(ArgParse::ParsedElement * f_parseTree, uint32_t f_default)
+ uint32_t getConnectTimeoutMs(std::string &f_connectTimeoutStr, uint32_t f_default)
{
// TODO: it would be nice to encode default values for options in the grammar
// TODO: also it would be nice to provide grammar/parsed elements which can output c++ types other than strings (integers, etc.)
- std::string connectTimeoutStr = f_parseTree->findFirstChild("connectTimeout");
uint32_t connectTimeoutMs = f_default;
- if(connectTimeoutStr != "")
+ if(f_connectTimeoutStr != "")
{
- connectTimeoutMs = std::stol(connectTimeoutStr);
+ connectTimeoutMs = std::stol(f_connectTimeoutStr);
}
return connectTimeoutMs;
}
diff --git a/src/libCli/libCli/cliUtils.hpp b/src/libCli/libCli/cliUtils.hpp
index 0556366..fa0b5fb 100644
--- a/src/libCli/libCli/cliUtils.hpp
+++ b/src/libCli/libCli/cliUtils.hpp
@@ -23,11 +23,11 @@ namespace cli
/// @returns true if channel is connected, false if timeout exceeded and channel is still not in connected state.
bool waitForChannelConnected(std::shared_ptr f_channel, uint32_t f_timeoutMs);
- /// Retrieves the "connectTimeout" option from the parse tree
- /// @param f_parseTree Parse-tree which should be searched for the option
+ /// Retrieves the "ConnectTimeout" option from the parse tree
+ /// @param f_connectTimeoutStr String holding the value for the timeout found in parsetree / config
/// @param f_default default value returned, if parse-tree did not contain the option.
/// @returns the value as an integer
- uint32_t getConnectTimeoutMs(ArgParse::ParsedElement * f_parseTree, uint32_t f_default = 500);
+ uint32_t getConnectTimeoutMs(std::string & f_connectTimeoutStr, uint32_t f_default = 500);
/// Convert a gRPC status code into a string.
/// @param f_statusCode The status code to convert.
diff --git a/src/libGwhisperSettings/CMakeLists.txt b/src/libGwhisperSettings/CMakeLists.txt
new file mode 100644
index 0000000..9b7f952
--- /dev/null
+++ b/src/libGwhisperSettings/CMakeLists.txt
@@ -0,0 +1,37 @@
+# Copyright 2022 IBM Corporation
+#
+# 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.
+
+include(FetchContent)
+FetchContent_Declare(json URL https://github.com/nlohmann/json/releases/download/v3.10.5/json.tar.xz)
+FetchContent_MakeAvailable(json)
+
+set(TARGET_NAME "libGwhisperSettings")
+set(TARGET_SRC
+ GwhisperSettings.cpp
+ )
+add_library(${TARGET_NAME} ${TARGET_SRC})
+
+target_link_libraries ( ${TARGET_NAME}
+ PRIVATE
+ reflection
+ protoDoc
+ )
+target_link_libraries ( ${TARGET_NAME}
+ PUBLIC
+ gwhisperUtils
+ DescDbProxy
+ ArgParse
+ nlohmann_json::nlohmann_json
+)
+target_include_directories(${TARGET_NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
\ No newline at end of file
diff --git a/src/libGwhisperSettings/GwhisperSettings.cpp b/src/libGwhisperSettings/GwhisperSettings.cpp
new file mode 100644
index 0000000..7e2291b
--- /dev/null
+++ b/src/libGwhisperSettings/GwhisperSettings.cpp
@@ -0,0 +1,109 @@
+// Copyright 2022 IBM Corporation
+//
+// 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.
+
+#include
+#include
+#include
+
+#include "GwhisperSettings.hpp"
+#include "libArgParse/ArgParse.hpp"
+#include "../utils/gwhisperUtils.hpp"
+
+using json = nlohmann::json;
+
+void gWhisperSettings::parseConfigFile(const std::string &f_inputFile){
+ // Parse JSON File into member SON object
+ std::ifstream ifs(f_inputFile.c_str());
+ std::string configString = gwhisper::util::readFromFile(f_inputFile);
+
+
+ /*if (!ifs.is_open())
+ {
+ std::cerr << "Error while opening config file at: " << f_inputFile << std::endl;
+ exit(EXIT_FAILURE);
+ }
+ // TODO: check for empty value with nlohmann::isempty()*/
+ try
+ {
+ m_config = json::parse(configString);
+ }
+ catch(json::parse_error)
+ {
+ std::cerr << "Error while parsing config file at " << f_inputFile << std::endl;
+ std::cerr << "Try checking for JSON Syntax errors or empty JSON file." << std::endl;
+ exit(EXIT_FAILURE);
+ }
+
+}
+
+std::string gWhisperSettings::findParameterSettingInConfig(const std::string &f_parameter,const json &f_startElement)
+{
+ for (const auto &item : f_startElement.items())
+ {
+ if(item.key() == f_parameter)
+ {
+ if(item.value().is_null())
+ {
+ return "";
+ }
+ else
+ {
+ return item.value();
+ }
+
+ }
+
+ // Only Call recursion, if current element contains further elements
+ if (item.value().is_object())
+ {
+ return findParameterSettingInConfig(f_parameter, item.value());
+ }
+ }
+ return "";
+}
+
+std::string gWhisperSettings::lookUpSetting(const std::string &f_parameter, ArgParse::ParsedElement &f_parseTree)
+{
+ bool containsParameter;
+ std::string setting;
+
+ if (f_parseTree.findFirstChild(f_parameter) != "")
+ {
+ return f_parseTree.findFirstChild(f_parameter);
+ }
+ else
+ {
+ setting = findParameterSettingInConfig(f_parameter, m_config);
+ return setting;
+ }
+}
+
+gWhisperSettings::gWhisperSettings(ArgParse::ParsedElement &f_parseTree){ //ParameterKey
+ if(m_config.is_null())
+ {
+ bool useCustomPath = f_parseTree.findFirstChild("ConfigFile")!= "";
+ const char* home = std::getenv("HOME");
+ std::string defaultPath = "/.config/gWhisperConfig.json";
+ std::string inputFile = home + defaultPath;
+
+ if(useCustomPath)
+ {
+ inputFile = f_parseTree.findFirstChild("ConfigFilePath");
+ }
+
+ parseConfigFile(inputFile);
+ }
+}
+
+gWhisperSettings::~gWhisperSettings(){}
\ No newline at end of file
diff --git a/src/libGwhisperSettings/GwhisperSettings.hpp b/src/libGwhisperSettings/GwhisperSettings.hpp
new file mode 100644
index 0000000..284f8a0
--- /dev/null
+++ b/src/libGwhisperSettings/GwhisperSettings.hpp
@@ -0,0 +1,52 @@
+// Copyright 2022 IBM Corporation
+//
+// 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.
+
+#pragma once
+#include
+#include "libArgParse/ArgParse.hpp"
+
+using json = nlohmann::json;
+
+class gWhisperSettings{
+ public:
+ /// Retrieves inital config object.
+ // Uses the parse tree only to retrieve the location of the config gile.
+ /// @param f_parseTree
+ gWhisperSettings(ArgParse::ParsedElement &f_parseTree);
+ ~gWhisperSettings();
+
+ // /Searches setting for a parameter in the parse tree.
+ /// Searches for in the config file, if parameter is not found in the parse tree.
+ /// @param f_parameter Name of paramter to retrieve seting for.
+ /// @param f_parseTree Current grammar tree.
+ /// @return Returns value of the parameter as string. Returns an empty string
+ /// if the parameter is not set.
+ std::string lookUpSetting(const std::string &f_parameter, ArgParse::ParsedElement &f_parseTree);
+
+
+ private:
+ /// Parses a valid json file into a json object.
+ /// @param f_inputFile Path to input file.
+ void parseConfigFile(const std::string &f_inputFile);
+
+ /// Recursively searches the value of a key in a (nested) json object
+ /// @param f_parameter Name of the parameter to search setting for.
+ /// @param f_startElement Node of config fike, from where the search starts.
+ /// @return returns setting for parameter as a string. If parameter is not set via config file or
+ /// the parameter is set tu null, an empty string is returned
+ std::string findParameterSettingInConfig(const std::string &f_parameter, const json &f_startElement);
+
+ json m_config;
+ std::vector m_configParameters;
+};
\ No newline at end of file
diff --git a/src/libLocalDescriptorCache/CMakeLists.txt b/src/libLocalDescriptorCache/CMakeLists.txt
index cc8dd8b..3d6ec02 100644
--- a/src/libLocalDescriptorCache/CMakeLists.txt
+++ b/src/libLocalDescriptorCache/CMakeLists.txt
@@ -28,6 +28,7 @@ target_link_libraries(${TARGET_NAME}
${TARGET_NAME}_protobuf
PRIVATE
+ libGwhisperSettings
gwhisperUtils
version
reflection
diff --git a/src/libLocalDescriptorCache/DescDbProxy.cpp b/src/libLocalDescriptorCache/DescDbProxy.cpp
index a8e25ed..d2692f6 100644
--- a/src/libLocalDescriptorCache/DescDbProxy.cpp
+++ b/src/libLocalDescriptorCache/DescDbProxy.cpp
@@ -17,6 +17,7 @@
#include "libCli/ConnectionManager.hpp"
#include "libArgParse/ArgParse.hpp"
#include "LocalDescDb.pb.h"
+#include "GwhisperSettings.hpp"
#include
#include
@@ -151,7 +152,9 @@ void DescDbProxy::repopulateLocalDb(localDescDb::Host& f_out_host, const std::st
void DescDbProxy::useReflection(const std::string &f_hostAddress)
{
- if (not cli::waitForChannelConnected(m_channel, cli::getConnectTimeoutMs(&m_parseTree)))
+ gWhisperSettings settingProxy(m_parseTree);
+ std::string connectTimeoutStr = settingProxy.lookUpSetting("ConnectTimeout", m_parseTree);
+ if (not cli::waitForChannelConnected(m_channel, cli::getConnectTimeoutMs(connectTimeoutStr)))
{
std::cerr << "Error: Could not establish Channel. Try checking network connection, hostname or SSL credentials." << std::endl;
exit(EXIT_FAILURE);
diff --git a/src/utils/gwhisperUtils.cpp b/src/utils/gwhisperUtils.cpp
index 3b12262..4714a13 100644
--- a/src/utils/gwhisperUtils.cpp
+++ b/src/utils/gwhisperUtils.cpp
@@ -31,7 +31,7 @@ namespace gwhisper
const char* home = std::getenv("HOME");
if (!home)
{
- std::cerr << "Error while fetching home envoronment. Try checking your home environment variable." << std::endl;
+ std::cerr << "Error while fetching home environment. Try checking your home environment variable." << std::endl;
exit(EXIT_FAILURE);
}
std::string subPath = f_path.substr(1); // take everything after '~'
diff --git a/testfile.json b/testfile.json
new file mode 100644
index 0000000..f724a8f
--- /dev/null
+++ b/testfile.json
@@ -0,0 +1,13 @@
+{
+ "configParameter": {
+ "Ssl": null,
+ "SslSettings": {
+ "ServerCertFile": null,
+ "ClientCertFile": null,
+ "ClientKeyFile": null
+ },
+ "DisableCache": null,
+ "RpcTimeoutInMs": null,
+ "connectTimeout": null
+ }
+}
\ No newline at end of file
diff --git a/tests/emptyConfig.json b/tests/emptyConfig.json
new file mode 100644
index 0000000..e69de29
diff --git a/tests/functionTests/CMakeLists.txt b/tests/functionTests/CMakeLists.txt
index 92dff17..8652ac7 100644
--- a/tests/functionTests/CMakeLists.txt
+++ b/tests/functionTests/CMakeLists.txt
@@ -33,4 +33,7 @@ add_test(NAME RpcTimeoutTests
add_test(NAME CacheTests
COMMAND python ${PROJECT_SOURCE_DIR}/tests/functionTests/runFunctionTest.py ${gwhisper_exec} ${testserver_exec} ${CMAKE_CURRENT_SOURCE_DIR}/resources/ ${PROJECT_BINARY_DIR}/tests/testServer/cert-key-pair/ ${PROJECT_SOURCE_DIR}/tests/functionTests/cacheFunctionTests.txt)
+add_test(NAME ConfigTests
+ COMMAND python ${PROJECT_SOURCE_DIR}/tests/functionTests/runFunctionTest.py ${gwhisper_exec} ${testserver_exec} ${CMAKE_CURRENT_SOURCE_DIR}/resources/ ${PROJECT_BINARY_DIR}/tests/testServer/cert-key-pair/ ${PROJECT_SOURCE_DIR}/tests/functionTests/configTests.txt)
+
file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/resources/data.bin DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/resources)
diff --git a/tests/functionTests/cacheFunctionTests.txt b/tests/functionTests/cacheFunctionTests.txt
index 02047bc..e3d21ab 100644
--- a/tests/functionTests/cacheFunctionTests.txt
+++ b/tests/functionTests/cacheFunctionTests.txt
@@ -90,14 +90,14 @@ RPC succeeded :D
rm ~/.cache/gwhisper/DescriptorCache.bin
#END_CMD
#EXEC_CMD
-@@CMD@@ --ssl --clientCert=@@PTC@@/client_crt.pem --serverCert=@@PTC@@/server_crt.pem localhost:50443 examples.NestedTypeRpcs echoNestedMaps
+@@CMD@@ --configFile=@@testResources@@/testDefaultConfig.json --ssl --clientCert=@@PTC@@/client_crt.pem --serverCert=@@PTC@@/server_crt.pem localhost:50443 examples.NestedTypeRpcs echoNestedMaps
/Error: Could not establish Channel. Try checking network connection, hostname or SSL credentials.*
#END_CMD
#END_TEST
#START_TEST Invalid secure RPC with existing cache
#EXEC_CMD
-@@CMD@@ --ssl --clientCert=@@PTC@@/client_crt.pem --clientKey=@@PTC@@/client_key.pem --serverCert=@@PTC@@/server_crt.pem localhost:50443 examples.ScalarTypeRpcs negateBool m_bool=0
+@@CMD@@ --configFile=@@testResources@@/testDefaultConfig.json --ssl --clientCert=@@PTC@@/client_crt.pem --clientKey=@@PTC@@/client_key.pem --serverCert=@@PTC@@/server_crt.pem localhost:50443 examples.ScalarTypeRpcs negateBool m_bool=0
/.* Received message:
| m_bool = true
RPC succeeded :D
@@ -107,7 +107,7 @@ ls ~/.cache/gwhisper/
DescriptorCache.bin
#END_CMD
#EXEC_CMD
-@@CMD@@ --ssl --clientCert=@@PTC@@/client_crt.pem --serverCert=@@PTC@@/server_crt.pem localhost:50443 examples.NestedTypeRpcs echoNestedMaps
+@@CMD@@ --configFile=@@testResources@@/testDefaultConfig.json --ssl --clientCert=@@PTC@@/client_crt.pem --serverCert=@@PTC@@/server_crt.pem localhost:50443 examples.NestedTypeRpcs echoNestedMaps
Error: channel connection attempt timed out
#END_CMD
#END_TEST
diff --git a/tests/functionTests/completionTests.txt b/tests/functionTests/completionTests.txt
index 14b6d78..8a2509c 100644
--- a/tests/functionTests/completionTests.txt
+++ b/tests/functionTests/completionTests.txt
@@ -23,6 +23,7 @@ Possible Candidates:
'--clientKey='
'--serverCert='
'--complete'
+ '--configFile='
'--debugComplete '
'--disableCache '
'--dot '
diff --git a/tests/functionTests/completionTests_fish.txt b/tests/functionTests/completionTests_fish.txt
index 138f656..f27fe45 100644
--- a/tests/functionTests/completionTests_fish.txt
+++ b/tests/functionTests/completionTests_fish.txt
@@ -24,6 +24,7 @@ Possible Candidates:
'--clientKey='
'--serverCert='
'--complete'
+ '--configFile='
'--debugComplete '
'--disableCache '
'--dot '
diff --git a/tests/functionTests/configTests.txt b/tests/functionTests/configTests.txt
new file mode 100644
index 0000000..ab8f26a
--- /dev/null
+++ b/tests/functionTests/configTests.txt
@@ -0,0 +1,55 @@
+# Copyright 2022 IBM Corporation
+#
+# 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.
+
+##############################################################################
+# Valid config file (.cache/gWhisperConfigFile.json)
+##############################################################################
+
+#START_TEST RPC using config settings with config File location
+@@CMD@@ --configFile=@@testResources@@/validConfig.json --disableCache --ssl localhost:50443 examples.NestedTypeRpcs echoNestedMaps
+Warning: no Fields found in parseTree for message 'NestedMaps'
+/.*Received message:
+| number_and_string..... = [NOT SET]
+| sub_message........... = [NOT SET]
+| simple_map_int[0/0] = {}
+| simple_map_string[0/0] = {}
+RPC succeeded :D
+#END_TEST
+
+#START_TEST RPC with cli settings that overide config settings (set Timeout to 100) in config
+@@CMD@@ --disableCache --configFile=@@testResources@@/validConfig.json --rpcTimeoutMilliseconds=2000 localhost:50051 examples.StatusHandling rpcSleepForSeconds number=1
+/.* Received message:
+
+RPC succeeded :D
+#END_TEST
+
+##############################################################################
+# Invalid config file
+##############################################################################
+#START_TEST RPC cannot open config file
+@@CMD@@ --configFile=@@testResources@@/validConfig.jso --complete localhost:50443 examples.StatusHandling neverEndingRpc
+/File not found at:.*
+#END_TEST
+
+#START_TEST Trying to open empty json file
+@@CMD@@ --configFile=@@testResources@@/emptyConfig.json --complete localhost:50051
+/Error while parsing config file at.*
+Try checking for JSON Syntax errors or empty JSON file.
+#END_TEST
+
+#START_TEST RPC with invalid json format in config File
+@@CMD@@ --configFile=@@testResources@@/invalidJsonConfig.json --complete localhost:50051
+/Error while parsing config file at.*
+Try checking for JSON Syntax errors or empty JSON file.
+#END_TEST
\ No newline at end of file
diff --git a/tests/functionTests/resources/emptyConfig.json b/tests/functionTests/resources/emptyConfig.json
new file mode 100644
index 0000000..e69de29
diff --git a/tests/functionTests/resources/invalidJsonConfig.json b/tests/functionTests/resources/invalidJsonConfig.json
new file mode 100644
index 0000000..8dbd36c
--- /dev/null
+++ b/tests/functionTests/resources/invalidJsonConfig.json
@@ -0,0 +1,17 @@
+{
+ "configParameter":
+ {
+ "Ssl":null
+ "SslSettings":
+ {
+ "ServerCertFile":null,
+ "ClientCertFile":null,
+ "ClientKeyFile":null
+ },
+
+ "DisableCache":null,
+
+ "RpcTimeoutInMs":"500",
+ "ConnectTimeout":"200"
+ }
+}
diff --git a/tests/functionTests/resources/testDefaultConfig.json b/tests/functionTests/resources/testDefaultConfig.json
new file mode 100644
index 0000000..03c2704
--- /dev/null
+++ b/tests/functionTests/resources/testDefaultConfig.json
@@ -0,0 +1,17 @@
+{
+ "configParameter":
+ {
+ "Ssl":null,
+ "SslSettings":
+ {
+ "ServerCertFile":null,
+ "ClientCertFile":null,
+ "ClientKeyFile":null
+ },
+
+ "DisableCache":null,
+
+ "RpcTimeoutInMs":null,
+ "ConnectTimeout":null
+ }
+}
\ No newline at end of file
diff --git a/tests/functionTests/resources/validConfig.json b/tests/functionTests/resources/validConfig.json
new file mode 100644
index 0000000..82a72a3
--- /dev/null
+++ b/tests/functionTests/resources/validConfig.json
@@ -0,0 +1,17 @@
+{
+ "configParameter":
+ {
+ "Ssl":null,
+ "SslSettings":
+ {
+ "ServerCertFile":"/home/anna/gWhisper/build/tests/testServer/cert-key-pair/server_crt.pem",
+ "ClientCertFile":"/home/anna/gWhisper/build/tests/testServer/cert-key-pair/client_crt.pem",
+ "ClientKeyFile":"/home/anna/gWhisper/build/tests/testServer/cert-key-pair/client_key.pem"
+ },
+
+ "DisableCache":null,
+
+ "RpcTimeoutInMs":"100",
+ "ConnectTimeout":"100"
+ }
+}
\ No newline at end of file
diff --git a/tests/functionTests/rpcTimeoutFunctionTests.txt b/tests/functionTests/rpcTimeoutFunctionTests.txt
index 5e9566a..6bd38bd 100644
--- a/tests/functionTests/rpcTimeoutFunctionTests.txt
+++ b/tests/functionTests/rpcTimeoutFunctionTests.txt
@@ -68,7 +68,7 @@ RPC succeeded :D
#END_TEST
#START_TEST check value default timeout for unary RPCs
-@@CMD@@ --disableCache localhost:50051 examples.StatusHandling rpcSleepForSeconds number=9
+@@CMD@@ --configFile=/@@testResources@@/testDefaultConfig.json --disableCache localhost:50051 examples.StatusHandling rpcSleepForSeconds number=9
/.* Received message:
RPC succeeded :D
diff --git a/tests/functionTests/sslFunctionTests.txt b/tests/functionTests/sslFunctionTests.txt
index f4cb899..22df62d 100644
--- a/tests/functionTests/sslFunctionTests.txt
+++ b/tests/functionTests/sslFunctionTests.txt
@@ -39,12 +39,12 @@ RPC succeeded :D
#END_TEST
#START_TEST secure gRPC with missing Certificate/Key and server requires cert-key-pair
-@@CMD@@ --disableCache --ssl --clientCert=@@PTC@@/client_crt.pem --serverCert=@@PTC@@/server_crt.pem localhost:50443 examples.NestedTypeRpcs echoNestedMaps
+@@CMD@@ --configFile=@@testResources@@/testDefaultConfig.json --disableCache --ssl --clientCert=@@PTC@@/client_crt.pem --serverCert=@@PTC@@/server_crt.pem localhost:50443 examples.NestedTypeRpcs echoNestedMaps
/Error: Could not establish Channel. Try checking network connection, hostname or SSL credentials.*
#END_TEST
#START_TEST secure gRPC with missing serverCert at default location
-@@CMD@@ --disableCache --ssl localhost:50052 examples.NestedTypeRpcs echoNestedMaps
+@@CMD@@ --configFile=@@testResources@@/testDefaultConfig.json --disableCache --ssl localhost:50052 examples.NestedTypeRpcs echoNestedMaps
/.*SSL_ERROR_SSL.*
?.*SSL_ERROR_SSL.*
/Error: Could not establish Channel. Try checking network connection, hostname or SSL credentials*
diff --git a/tests/invalidJsonConfig.json b/tests/invalidJsonConfig.json
new file mode 100644
index 0000000..8dbd36c
--- /dev/null
+++ b/tests/invalidJsonConfig.json
@@ -0,0 +1,17 @@
+{
+ "configParameter":
+ {
+ "Ssl":null
+ "SslSettings":
+ {
+ "ServerCertFile":null,
+ "ClientCertFile":null,
+ "ClientKeyFile":null
+ },
+
+ "DisableCache":null,
+
+ "RpcTimeoutInMs":"500",
+ "ConnectTimeout":"200"
+ }
+}