Allow unescaped quoted strings with --config CLI (#2835)

* Parsing issue with --config CLI

* Allow original behaviour with escaped quotations

* Support arrays

* Formatting
This commit is contained in:
Wesley Shillingford 2020-07-23 11:19:47 +01:00 committed by GitHub
commit facc103527
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 120 additions and 4 deletions

View file

@ -1,3 +1,4 @@
#include <nano/lib/cli.hpp>
#include <nano/node/cli.hpp>
#include <nano/secure/utility.hpp>
#include <nano/test_common/testutil.hpp>
@ -43,6 +44,31 @@ TEST (cli, key_create)
ASSERT_EQ (vals[2], public_key.to_account ());
}
TEST (cli, config_override_parsing)
{
std::vector<nano::config_key_value_pair> key_value_pairs;
auto config_overrides = nano::config_overrides (key_value_pairs);
ASSERT_TRUE (config_overrides.empty ());
key_value_pairs.push_back ({ "key", "value" });
config_overrides = nano::config_overrides (key_value_pairs);
ASSERT_EQ (config_overrides[0], "key=\"value\"");
key_value_pairs.push_back ({ "node.online_weight_minimum", "40000000000000000000000000000000000000" });
config_overrides = nano::config_overrides (key_value_pairs);
ASSERT_EQ (config_overrides[1], "node.online_weight_minimum=\"40000000000000000000000000000000000000\"");
// Should add this as it contains escaped quotes, and make sure these are not escaped again
key_value_pairs.push_back ({ "key", "\"value\"" });
config_overrides = nano::config_overrides (key_value_pairs);
ASSERT_EQ (config_overrides[2], "key=\"value\"");
ASSERT_EQ (config_overrides.size (), 3);
// Try it with arrays, with and without escaped quotes
key_value_pairs.push_back ({ "node.work_peers", "[127.0.0.1:7000,\"128.0.0.1:50000\"]" });
config_overrides = nano::config_overrides (key_value_pairs);
ASSERT_EQ (config_overrides[3], "node.work_peers=[\"127.0.0.1:7000\",\"128.0.0.1:50000\"]");
ASSERT_EQ (config_overrides.size (), 4);
}
namespace
{
std::string call_cli_command (boost::program_options::variables_map const & vm)

View file

@ -20,6 +20,8 @@ add_library (nano_lib
blockbuilders.cpp
blocks.hpp
blocks.cpp
cli.hpp
cli.cpp
config.hpp
config.cpp
configbase.hpp

66
nano/lib/cli.cpp Normal file
View file

@ -0,0 +1,66 @@
#include <nano/lib/cli.hpp>
#include <boost/algorithm/string.hpp>
#include <boost/format.hpp>
#include <sstream>
std::vector<std::string> nano::config_overrides (std::vector<config_key_value_pair> const & key_value_pairs_a)
{
std::vector<std::string> overrides;
auto format (boost::format ("%1%=%2%"));
auto format_add_escaped_quotes (boost::format ("%1%=\"%2%\""));
for (auto pair : key_value_pairs_a)
{
auto start = pair.value.find ('[');
std::string value;
auto is_array = (start != std::string::npos);
if (is_array)
{
// Trim off the square brackets [] of the array
auto end = pair.value.find (']');
auto array_values = pair.value.substr (start + 1, end - start - 1);
// Split the string by comma
std::vector<std::string> split_elements;
boost::split (split_elements, array_values, boost::is_any_of (","));
auto format (boost::format ("%1%"));
auto format_add_escaped_quotes (boost::format ("\"%1%\""));
// Rebuild the array string adding escaped quotes if necessary
std::ostringstream ss;
ss << "[";
for (auto i = 0; i < split_elements.size (); ++i)
{
auto & elem = split_elements[i];
auto already_escaped = elem.find ('\"') != std::string::npos;
ss << ((!already_escaped ? format_add_escaped_quotes : format) % elem).str ();
if (i != split_elements.size () - 1)
{
ss << ",";
}
}
ss << "]";
value = ss.str ();
}
else
{
value = pair.value;
}
auto already_escaped = value.find ('\"') != std::string::npos;
overrides.push_back (((!already_escaped ? format_add_escaped_quotes : format) % pair.key % value).str ());
}
return overrides;
}
std::istream & nano::operator>> (std::istream & is, nano::config_key_value_pair & into)
{
char ch;
while (is >> ch && ch != '=')
{
into.key += ch;
}
return is >> into.value;
}

19
nano/lib/cli.hpp Normal file
View file

@ -0,0 +1,19 @@
#pragma once
#include <iostream>
#include <string>
#include <vector>
namespace nano
{
class config_key_value_pair
{
public:
std::string key;
std::string value;
};
std::vector<std::string> config_overrides (std::vector<config_key_value_pair> const & key_value_pairs_a);
std::istream & operator>> (std::istream & is, nano::config_key_value_pair & into);
}

View file

@ -1,4 +1,5 @@
#include <nano/crypto_lib/random_pool.hpp>
#include <nano/lib/cli.hpp>
#include <nano/lib/utility.hpp>
#include <nano/nano_node/daemon.hpp>
#include <nano/node/cli.hpp>
@ -68,7 +69,7 @@ int main (int argc, char * const * argv)
description.add_options ()
("help", "Print out options")
("version", "Prints out version")
("config", boost::program_options::value<std::vector<std::string>>()->multitoken(), "Pass node configuration values. This takes precedence over any values in the configuration file. This option can be repeated multiple times.")
("config", boost::program_options::value<std::vector<nano::config_key_value_pair>>()->multitoken(), "Pass node configuration values. This takes precedence over any values in the configuration file. This option can be repeated multiple times.")
("daemon", "Start node daemon")
("compare_rep_weights", "Display a summarized comparison between the hardcoded bootstrap weights and representative weights from the ledger. Full comparison is output to logs")
("debug_block_count", "Display the number of block")

View file

@ -1,3 +1,4 @@
#include <nano/lib/cli.hpp>
#include <nano/lib/errors.hpp>
#include <nano/lib/threading.hpp>
#include <nano/lib/utility.hpp>
@ -90,7 +91,7 @@ int main (int argc, char * const * argv)
// clang-format off
description.add_options ()
("help", "Print out options")
("config", boost::program_options::value<std::vector<std::string>>()->multitoken(), "Pass RPC configuration values. This takes precedence over any values in the configuration file. This option can be repeated multiple times.")
("config", boost::program_options::value<std::vector<nano::config_key_value_pair>>()->multitoken(), "Pass RPC configuration values. This takes precedence over any values in the configuration file. This option can be repeated multiple times.")
("daemon", "Start RPC daemon")
("data_path", boost::program_options::value<std::string> (), "Use the supplied path as the data directory")
("network", boost::program_options::value<std::string> (), "Use the supplied network (live, beta or test)")
@ -139,7 +140,7 @@ int main (int argc, char * const * argv)
auto config (vm.find ("config"));
if (config != vm.end ())
{
config_overrides = config->second.as<std::vector<std::string>> ();
config_overrides = nano::config_overrides (config->second.as<std::vector<nano::config_key_value_pair>> ());
}
run (data_path, config_overrides);
}

View file

@ -1,3 +1,4 @@
#include <nano/lib/cli.hpp>
#include <nano/lib/tomlconfig.hpp>
#include <nano/node/cli.hpp>
#include <nano/node/common.hpp>
@ -170,7 +171,7 @@ std::error_code nano::update_flags (nano::node_flags & flags_a, boost::program_o
auto config (vm.find ("config"));
if (config != vm.end ())
{
flags_a.config_overrides = config->second.as<std::vector<std::string>> ();
flags_a.config_overrides = nano::config_overrides (config->second.as<std::vector<nano::config_key_value_pair>> ());
}
return ec;
}