1 #include "configuration.hpp"
19 std::ostringstream os;
20 os <<
"Server ID value " <<
value <<
" is not in the valid range [1 .. 255]";
21 throw std::out_of_range(os.str());
24 std::ostream& operator<<(std::ostream& os,
const server_id&
self)
26 return os <<
self.value;
32 constexpr std::size_t not_a_line = ~0UL;
34 class zero_copy_streambuf final :
38 zero_copy_streambuf(string_view input)
42 setg(p, p, p + input.size());
58 configuration::setting<T>::setting() noexcept :
64 configuration::setting<T>::setting(T value, std::size_t line) noexcept :
65 value(std::move(value)),
69 configuration::configuration() =
default;
71 configuration::~configuration() noexcept = default;
77 .client_port(client_port)
78 .init_limit(default_init_limit)
79 .sync_limit(default_sync_limit)
86 static const std::regex line_expr(R
"(^([^=]+)=([^ #]+)[ #]*$)",
87 std::regex_constants::ECMAScript | std::regex_constants::optimize
89 constexpr auto name_idx = 1U;
90 constexpr
auto data_idx = 2U;
94 for (std::size_t line_no = 0U; line_no < lines.size(); ++line_no)
96 const auto& line = lines[line_no];
98 if (line.empty() || line[0] ==
'#')
102 if (std::regex_match(line, match, line_expr))
104 auto name = match[name_idx].str();
105 auto data = match[data_idx].str();
107 if (name ==
"clientPort")
109 out._client_port = { std::uint16_t(std::atoi(data.c_str())), line_no };
111 else if (name ==
"dataDir")
113 out._data_directory = { std::move(data), line_no };
115 else if (name ==
"tickTime")
117 out._tick_time = { std::chrono::milliseconds(std::atol(data.c_str())), line_no };
119 else if (name ==
"initLimit")
121 out._init_limit = { std::size_t(std::atol(data.c_str())), line_no };
123 else if (name ==
"syncLimit")
125 out._sync_limit = { std::size_t(std::atol(data.c_str())), line_no };
127 else if (name ==
"leaderServes")
129 out._leader_serves = { (data ==
"yes"), line_no };
131 else if (name.find(
"server.") == 0U)
133 auto id = std::size_t(std::atol(name.c_str() + 7));
134 out._server_paths.insert({
server_id(
id), { std::move(data), line_no } });
138 out._unknown_settings.insert({ std::move(name), { std::move(data), line_no } });
143 out._lines = std::move(lines);
149 std::vector<std::string> lines;
152 while(std::getline(stream, line))
154 lines.emplace_back(std::move(line));
160 throw std::runtime_error(
"Loading configuration did not reach EOF");
165 std::ifstream inf(filename.c_str());
167 out._source_file = std::move(filename);
173 zero_copy_streambuf buff(value);
174 std::istream stream(&buff);
180 return _data_directory.value
181 && _client_port.value
184 && _lines.size() == 4U;
187 template <
typename T,
typename FEncode>
188 void configuration::set(setting<T>& target, optional<T> value, string_view key,
const FEncode& encode)
190 std::string target_line;
193 std::ostringstream os;
194 os << key <<
'=' << encode(*value);
195 target_line = os.str();
198 if (target.line == not_a_line && value)
200 target.value = std::move(value);
201 target.line = _lines.size();
202 _lines.emplace_back(std::move(target_line));
204 else if (target.line == not_a_line && !value)
210 target.value = std::move(value);
211 _lines[target.line] = std::move(target_line);
215 template <
typename T>
216 void configuration::set(setting<T>& target, optional<T> value, string_view key)
218 return set(target, std::move(value), key, [] (
const T& x) ->
const T& {
return x; });
228 set(_client_port, port,
"clientPort");
234 return _data_directory.value;
239 set(_data_directory, std::move(path),
"dataDir");
250 set(_tick_time, tick_time,
"tickTime", [] (duration_type x) {
return x.count(); });
261 set(_init_limit, limit,
"initLimit");
272 set(_sync_limit, limit,
"syncLimit");
278 return _leader_serves.value;
283 set(_leader_serves, serve,
"leaderServes", [] (
bool x) {
return x ?
"yes" :
"no"; });
289 std::map<server_id, std::string> out;
290 for (
const auto& entry : _server_paths)
291 out.insert({ entry.first, *entry.second.value });
297 std::string hostname,
298 std::uint16_t peer_port,
299 std::uint16_t leader_port
304 if (_server_paths.count(
id))
305 throw std::runtime_error(std::string(
"Already a server with ID ") + std::to_string(
id.value));
308 hostname += std::to_string(peer_port);
310 hostname += std::to_string(leader_port);
312 auto iter = _server_paths.emplace(
id, setting<std::string>()).first;
313 set(iter->second, some(std::move(hostname)), std::string(
"server.") + std::to_string(iter->first.value));
319 std::map<std::string, std::string> out;
320 for (
const auto& entry : _unknown_settings)
321 out.insert({ entry.first, *entry.second.value });
334 lines.emplace_back(key +
"=" + value);
343 for (
const auto& line : _lines)
344 os << line << std::endl;
351 std::ofstream ofs(filename.c_str());
354 _source_file = std::move(filename);
356 throw std::runtime_error(
"Error saving file");
364 auto same_items = [] (
const auto& a,
const auto& b)
366 return a.first == b.first
367 && a.second.value == b.second.value;
376 && lhs._server_paths.size() == rhs._server_paths.size()
377 && lhs._server_paths.end() == std::mismatch(lhs._server_paths.begin(), lhs._server_paths.end(),
378 rhs._server_paths.begin(), rhs._server_paths.end(),
381 && lhs._unknown_settings.size() == rhs._unknown_settings.size()
382 && lhs._unknown_settings.end() == std::mismatch(lhs._unknown_settings.begin(), lhs._unknown_settings.end(),
383 rhs._unknown_settings.begin(), rhs._unknown_settings.end(),
391 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.
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.
static std::uint16_t default_leader_port
The default value for leader_port.
static std::uint16_t default_client_port
The default value for client_port.
static std::size_t default_sync_limit
The default value for sync_limit.
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.
duration_type tick_time() const
static configuration from_string(string_view value)
Load configuration directly from the in-memory value.
std::size_t init_limit() const
static std::size_t default_init_limit
The default value for init_limit.
void ensure_valid() const
Check that this ID is a valid one.
static duration_type default_tick_time
The default value for tick_time.
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)
void save_file(std::string filename)
Save this configuration to filename.
static std::uint16_t default_peer_port
The default value for peer_port.
std::size_t sync_limit() const
std::uint16_t client_port() const
optional< bool > leader_serves() 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.
configuration & add_setting(std::string key, std::string value)
Add an arbitrary setting with the key and value.