1 #include "configuration.hpp"
20 std::ostringstream os;
21 os <<
"Server ID value " <<
value <<
" is not in the valid range [1 .. 255]";
22 throw std::out_of_range(os.str());
25 std::ostream& operator<<(std::ostream& os,
const server_id&
self)
27 return os <<
self.value;
33 constexpr std::size_t not_a_line = ~0UL;
35 class zero_copy_streambuf final :
39 zero_copy_streambuf(string_view input)
43 setg(p, p, p + input.size());
62 configuration::setting<T>::setting() noexcept :
68 configuration::setting<T>::setting(T value, std::size_t line) noexcept :
69 value(std::move(value)),
73 configuration::configuration() =
default;
75 configuration::~configuration() noexcept = default;
81 .client_port(client_port)
82 .init_limit(default_init_limit)
83 .sync_limit(default_sync_limit)
88 static std::set<std::string> parse_whitelist(string_view source)
90 std::set<std::string> out;
92 while (!source.empty())
94 auto idx = source.find_first_of(
',');
95 if (idx == string_view::npos)
100 source.remove_prefix(1);
104 auto sub = source.substr(0, idx);
105 source.remove_prefix(idx);
106 while (!sub.empty() && std::isspace(sub.front()))
107 sub.remove_prefix(1);
109 while (!sub.empty() && std::isspace(sub.back()))
110 sub.remove_suffix(1);
112 out.insert(std::string(sub));
120 static const std::regex line_expr(R
"(^([^=]+)=([^ #]+)[ #]*$)",
121 std::regex_constants::ECMAScript | std::regex_constants::optimize
123 constexpr auto name_idx = 1U;
124 constexpr
auto data_idx = 2U;
128 for (std::size_t line_no = 0U; line_no < lines.size(); ++line_no)
130 const auto& line = lines[line_no];
132 if (line.empty() || line[0] ==
'#')
136 if (std::regex_match(line, match, line_expr))
138 auto name = match[name_idx].str();
139 auto data = match[data_idx].str();
141 if (name ==
"clientPort")
143 out._client_port = { std::uint16_t(std::atoi(data.c_str())), line_no };
145 else if (name ==
"dataDir")
147 out._data_directory = { std::move(data), line_no };
149 else if (name ==
"tickTime")
151 out._tick_time = { std::chrono::milliseconds(std::atol(data.c_str())), line_no };
153 else if (name ==
"initLimit")
155 out._init_limit = { std::size_t(std::atol(data.c_str())), line_no };
157 else if (name ==
"syncLimit")
159 out._sync_limit = { std::size_t(std::atol(data.c_str())), line_no };
161 else if (name ==
"leaderServes")
163 out._leader_serves = { (data ==
"yes"), line_no };
165 else if (name ==
"4lw.commands.whitelist")
167 out._four_letter_word_whitelist = { parse_whitelist(data), line_no };
169 else if (name.find(
"server.") == 0U)
171 auto id = std::size_t(std::atol(name.c_str() + 7));
172 out._server_paths.insert({
server_id(
id), { std::move(data), line_no } });
176 out._unknown_settings.insert({ std::move(name), { std::move(data), line_no } });
181 out._lines = std::move(lines);
187 std::vector<std::string> lines;
190 while(std::getline(stream, line))
192 lines.emplace_back(std::move(line));
198 throw std::runtime_error(
"Loading configuration did not reach EOF");
203 std::ifstream inf(filename.c_str());
205 out._source_file = std::move(filename);
211 zero_copy_streambuf buff(value);
212 std::istream stream(&buff);
218 return _data_directory.value
219 && _client_port.value
222 && _lines.size() == 4U;
225 template <
typename T,
typename FEncode>
226 void configuration::set(setting<T>& target, optional<T> value, string_view key,
const FEncode& encode)
228 std::string target_line;
231 std::ostringstream os;
232 os << key <<
'=' << encode(*value);
233 target_line = os.str();
236 if (target.line == not_a_line && value)
238 target.value = std::move(value);
239 target.line = _lines.size();
240 _lines.emplace_back(std::move(target_line));
242 else if (target.line == not_a_line && !value)
248 target.value = std::move(value);
249 _lines[target.line] = std::move(target_line);
253 template <
typename T>
254 void configuration::set(setting<T>& target, optional<T> value, string_view key)
256 return set(target, std::move(value), key, [] (
const T& x) ->
const T& {
return x; });
266 set(_client_port, port,
"clientPort");
272 return _data_directory.value;
277 set(_data_directory, std::move(path),
"dataDir");
288 set(_tick_time, tick_time,
"tickTime", [] (duration_type x) {
return x.count(); });
299 set(_init_limit, limit,
"initLimit");
310 set(_sync_limit, limit,
"syncLimit");
316 return _leader_serves.value.value_or(
true);
321 set(_leader_serves, serve,
"leaderServes", [] (
bool x) {
return x ?
"yes" :
"no"; });
327 if (_four_letter_word_whitelist.value)
328 return *_four_letter_word_whitelist.value;
335 if (words && words->size() > 1U && words->count(
"*"))
336 throw std::invalid_argument(
"");
338 set(_four_letter_word_whitelist,
340 "4lw.commands.whitelist",
341 [] (
const std::set<std::string>& words)
344 std::ostringstream os;
346 for (
const auto& word : words)
348 if (!std::exchange(first,
false))
361 std::map<server_id, std::string> out;
362 for (
const auto& entry : _server_paths)
363 out.insert({ entry.first, *entry.second.value });
369 std::string hostname,
370 std::uint16_t peer_port,
371 std::uint16_t leader_port
376 if (_server_paths.count(
id))
377 throw std::runtime_error(std::string(
"Already a server with ID ") + std::to_string(
id.value));
380 hostname += std::to_string(peer_port);
382 hostname += std::to_string(leader_port);
384 auto iter = _server_paths.emplace(
id, setting<std::string>()).first;
385 set(iter->second, some(std::move(hostname)), std::string(
"server.") + std::to_string(iter->first.value));
391 std::map<std::string, std::string> out;
392 for (
const auto& entry : _unknown_settings)
393 out.insert({ entry.first, *entry.second.value });
406 lines.emplace_back(key +
"=" + value);
415 for (
const auto& line : _lines)
416 os << line << std::endl;
423 std::ofstream ofs(filename.c_str());
426 _source_file = std::move(filename);
428 throw std::runtime_error(
"Error saving file");
436 auto same_items = [] (
const auto& a,
const auto& b)
438 return a.first == b.first
439 && a.second.value == b.second.value;
449 && lhs._server_paths.size() == rhs._server_paths.size()
450 && lhs._server_paths.end() == std::mismatch(lhs._server_paths.begin(), lhs._server_paths.end(),
451 rhs._server_paths.begin(), rhs._server_paths.end(),
454 && lhs._unknown_settings.size() == rhs._unknown_settings.size()
455 && lhs._unknown_settings.end() == std::mismatch(lhs._unknown_settings.begin(), lhs._unknown_settings.end(),
456 rhs._unknown_settings.begin(), rhs._unknown_settings.end(),
464 return !(lhs == rhs);
T * ptr
A simple, unowned pointer.
std::map< server_id, std::string > servers() const
Get the servers which are part of the ZooKeeper ensemble.
Represents the ID of a server in the ensemble.
static const std::size_t default_sync_limit
The default value for sync_limit.
const optional< std::string > & source_file() const
Get the source file. This will only have a value if this was created by from_file.
bool is_minimal() const
Check if this is a "minimal" configuration – meaning it only has a data_directory and client_port se...
const optional< std::string > & data_directory() const
static configuration from_lines(std::vector< std::string > lines)
Load configuration from the provided lines.
bool leader_serves() const
static const std::set< std::string > all_four_letter_word_whitelist
A value for four_letter_word_whitelist that enables all commands.
const std::set< std::string > & four_letter_word_whitelist() const
std::map< std::string, std::string > unknown_settings() const
Get settings that were in the configuration file (or manually added with add_setting) but unknown to ...
static configuration from_file(std::string filename)
Load the configuration from a file.
static configuration from_stream(std::istream &stream)
Load configuration from the provided stream.
static const std::uint16_t default_client_port
The default value for client_port.
static const std::uint16_t default_leader_port
The default value for leader_port.
duration_type tick_time() const
static const std::size_t default_init_limit
The default value for init_limit.
static const std::uint16_t default_peer_port
The default value for peer_port.
static configuration from_string(string_view value)
Load configuration directly from the in-memory value.
std::size_t init_limit() const
void ensure_valid() const
Check that this ID is a valid one.
Represents a configuration which should be run by server instance.
void save(std::ostream &stream) const
Write this configuration to the provided stream.
friend bool operator==(const configuration &lhs, const configuration &rhs)
static const duration_type default_tick_time
The default value for tick_time.
void save_file(std::string filename)
Save this configuration to filename.
std::size_t sync_limit() const
std::uint16_t client_port() const
configuration & add_server(server_id id, std::string hostname, std::uint16_t peer_port=default_peer_port, std::uint16_t leader_port=default_leader_port)
Add a new server to the configuration.
value_type value
Underlying value of this ID.
static const std::set< std::string > default_four_letter_word_whitelist
The default value for four_letter_word_whitelist.
configuration & add_setting(std::string key, std::string value)
Add an arbitrary setting with the key and value.