Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Issue 47 #141

Open
wants to merge 20 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
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
48 changes: 48 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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:

Expand All @@ -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 <br> null: Disable secure connection |
| SslSettings | "AbsolutePath" (string): find path with pwd<br> null: |
| DisableCache | "Yes" (string) : Enable a secure connection to the server <br> null: Disable secure connection |
| RpcTimeoutInMs | "TimeInMs" (string): <br> null: |
| ConnectTimeout | "TimeInMs" (string): <br> 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=<yourConfigPath>` when executing gwhisper.
1 change: 1 addition & 0 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ add_subdirectory(gwhisper)
add_subdirectory(version)
anna-riesch marked this conversation as resolved.
Show resolved Hide resolved
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)
Expand Down
1 change: 1 addition & 0 deletions src/gwhisper/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ target_link_libraries ( ${TARGET_NAME}
reflection
version
generateHelpString
libGwhisperSettings
)

set_target_properties(
Expand Down
6 changes: 4 additions & 2 deletions src/gwhisper/gwhisper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
#include <libCli/Call.hpp>
#include <libCli/Completion.hpp>
#include <versionDefine.h> // generated during build
#include "GwhisperSettings.hpp"

using namespace ArgParse;

Expand Down Expand Up @@ -56,14 +57,15 @@ 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") != "")
{
std::cout << grammarPool.getDotGraph();
return 0;
}

if (parseTree.findFirstChild("Complete") != "")
if (paramProxy.lookUpSetting("Complete", parseTree) != "")
{
bool completeDebug = (parseTree.findFirstChild("CompleteDebug") != "");
if (parseTree.findFirstChild("fish") != "")
Expand Down Expand Up @@ -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;
}

Expand Down
5 changes: 5 additions & 0 deletions src/libCli/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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})

Expand Down
35 changes: 22 additions & 13 deletions src/libCli/Call.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
#include <ctime>
#include <iomanip>
#include <optional>
#include "GwhisperSettings.hpp"

// for detecting if we are writing stdout to terminal or to pipe/file
#include <stdio.h>
Expand All @@ -32,6 +33,7 @@
#include <libCli/cliUtils.hpp>

using namespace ArgParse;
using json = nlohmann::json;

namespace cli
{
Expand All @@ -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<MessageFormatter> createMessageFormatter(ParsedElement &parseTree)
std::unique_ptr<MessageFormatter> createMessageFormatter(ParsedElement &parseTree, gWhisperSettings f_settingProxy)
{
if(parseTree.findFirstChild("JsonOutput") != "")
if(f_settingProxy.lookUpSetting("JsonOutput", parseTree) != "")
{
return std::make_unique<MessageFormatterJson>();
}
Expand All @@ -59,21 +61,23 @@ namespace cli
auto humanFormatter = std::make_unique<MessageFormatterOptimizedForHumans>();

// 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();
}

// 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();
}
Expand Down Expand Up @@ -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;
Expand All @@ -121,7 +126,8 @@ namespace cli

std::shared_ptr<grpc::Channel> 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;
Expand Down Expand Up @@ -152,8 +158,7 @@ namespace cli
std::optional<std::chrono::time_point<std::chrono::system_clock>> deadline;
std::chrono::time_point<std::chrono::system_clock> defaultDeadline = std::chrono::system_clock::now() + std::chrono::milliseconds(30000);


bool setTimeout = (parseTree.findFirstChild("rpcTimeout") != "");
bool setTimeout = (settingProxy.lookUpSetting("RpcTimeoutInMs", parseTree) != "");

if(!setTimeout)
{
Expand All @@ -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)
{
Expand All @@ -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
Expand All @@ -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;
Expand Down
16 changes: 6 additions & 10 deletions src/libCli/ConnectionManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
#include <grpcpp/grpcpp.h>
#include <grpcpp/security/credentials.h>
#include <libArgParse/ArgParse.hpp>
#include "GwhisperSettings.hpp"

namespace cli
{
Expand Down Expand Up @@ -122,17 +123,13 @@ namespace cli
ConnList connection;
std::shared_ptr<grpc::ChannelCredentials> creds;
std::shared_ptr<grpc::ChannelCredentials> 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);
Expand All @@ -145,7 +142,6 @@ namespace cli
}

bool disableCache = (f_parseTree.findFirstChild("DisableCache") != "");
// Timeout as chrono duration

connection.descDbProxy = std::make_shared<DescDbProxy>(disableCache, f_serverAddress, connection.channel, f_parseTree);
connection.descPool = std::make_shared<grpc::protobuf::DescriptorPool>(connection.descDbProxy.get());
Expand Down
23 changes: 14 additions & 9 deletions src/libCli/GrammarConstruction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -455,21 +455,21 @@ namespace cli
GrammarElement *optionsalt = f_grammarPool.createElement<Alternation>();
optionsalt->addChild(f_grammarPool.createElement<FixedString>("-h", "Help"));
optionsalt->addChild(f_grammarPool.createElement<FixedString>("--help", "Help"));
optionsalt->addChild(f_grammarPool.createElement<FixedString>("--ssl", "ssl"));
optionsalt->addChild(f_grammarPool.createElement<FixedString>("--ssl", "Ssl"));

GrammarElement *clientCert = f_grammarPool.createElement<Concatenation>();
clientCert->addChild(f_grammarPool.createElement<FixedString>("--clientCert=", "OptionClientCert"));
clientCert->addChild(f_grammarPool.createElement<EscapedString>(" %", '%', "FileClientCert"));
clientCert->addChild(f_grammarPool.createElement<EscapedString>(" %", '%', "ClientCertFile"));
optionsalt->addChild(clientCert);

GrammarElement *clientKey = f_grammarPool.createElement<Concatenation>();
clientKey->addChild(f_grammarPool.createElement<FixedString>("--clientKey=", "OptionClientKey"));
clientKey->addChild(f_grammarPool.createElement<EscapedString>(" %", '%', "FileClientKey"));
clientKey->addChild(f_grammarPool.createElement<EscapedString>(" %", '%', "ClientKeyFile"));
optionsalt->addChild(clientKey);

GrammarElement *serverCert = f_grammarPool.createElement<Concatenation>();
serverCert->addChild(f_grammarPool.createElement<FixedString>("--serverCert=", "OptionServerCert"));
serverCert->addChild(f_grammarPool.createElement<EscapedString>(" %", '%', "FileServerCert"));
serverCert->addChild(f_grammarPool.createElement<EscapedString>(" %", '%', "ServerCertFile"));
optionsalt->addChild(serverCert);

GrammarElement *completeOption = f_grammarPool.createElement<Concatenation>();
Expand All @@ -485,6 +485,11 @@ namespace cli
completeDialectChoice->addChild(f_grammarPool.createElement<FixedString>("fish", "fish"));
optionsalt->addChild(completeOption);

GrammarElement *config = f_grammarPool.createElement<Concatenation>();
config->addChild(f_grammarPool.createElement<FixedString>("--configFile=", "ConfigFile"));
config->addChild(f_grammarPool.createElement<EscapedString>(" %", '%', "ConfigFilePath"));
optionsalt->addChild(config);

//completeOption->addChild(f_grammarPool.createElement<FixedString>("--complete", "Complete"));
optionsalt->addChild(f_grammarPool.createElement<FixedString>("--debugComplete", "CompleteDebug"));
optionsalt->addChild(f_grammarPool.createElement<FixedString>("--disableCache", "DisableCache"));
Expand All @@ -501,19 +506,19 @@ namespace cli
optionsalt->addChild(f_grammarPool.createElement<FixedString>("--noSimpleMapOutput", "NoSimpleMapOutput"));

GrammarElement *timeout = f_grammarPool.createElement<Concatenation>();
timeout->addChild(f_grammarPool.createElement<FixedString>("--rpcTimeoutMilliseconds", "rpcTimeout"));
timeout->addChild(f_grammarPool.createElement<FixedString>("--rpcTimeoutMilliseconds")); //RPCTimeout
timeout->addChild(f_grammarPool.createElement<FixedString>("="));
GrammarElement *timeoutTime = f_grammarPool.createElement<Alternation>();
GrammarElement *timeoutTime = f_grammarPool.createElement<Alternation>("RpcTimeoutInMs");
timeout->addChild(timeoutTime);
timeoutTime->addChild(f_grammarPool.createElement<RegEx>("[0-9]+", "rpcTimeoutInMs"));
timeoutTime->addChild(f_grammarPool.createElement<RegEx>("[0-9]+")); // RpcTimeoutInMs
GrammarElement *manualInfiniteTimeout = f_grammarPool.createElement<Optional>();
timeoutTime->addChild(manualInfiniteTimeout);
manualInfiniteTimeout->addChild(f_grammarPool.createElement<FixedString>("None", "manualInfiniteTimeout"));
manualInfiniteTimeout->addChild(f_grammarPool.createElement<FixedString>("None")); // manualInfiniteTimeout
optionsalt->addChild(timeout);

GrammarElement *timeoutOption = f_grammarPool.createElement<Concatenation>();
timeoutOption->addChild(f_grammarPool.createElement<FixedString>("--connectTimeoutMilliseconds="));
timeoutOption->addChild(f_grammarPool.createElement<RegEx>("[0-9]+", "connectTimeout"));
timeoutOption->addChild(f_grammarPool.createElement<RegEx>("[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.
Expand Down
7 changes: 3 additions & 4 deletions src/libCli/cliUtils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down
6 changes: 3 additions & 3 deletions src/libCli/libCli/cliUtils.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<grpc::Channel> 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.
Expand Down
Loading