Line data Source code
1 : #include "source/server/options_impl.h"
2 :
3 : #include <chrono>
4 : #include <cstdint>
5 : #include <iostream>
6 : #include <string>
7 :
8 : #include "envoy/admin/v3/server_info.pb.h"
9 :
10 : #include "source/common/common/fmt.h"
11 : #include "source/common/common/logger.h"
12 : #include "source/common/common/macros.h"
13 : #include "source/common/protobuf/utility.h"
14 : #include "source/common/stats/tag_utility.h"
15 : #include "source/common/version/version.h"
16 : #include "source/server/options_impl_platform.h"
17 :
18 : #include "absl/strings/str_replace.h"
19 : #include "absl/strings/str_split.h"
20 : #include "absl/strings/string_view.h"
21 : #include "spdlog/spdlog.h"
22 : #include "tclap/CmdLine.h"
23 :
24 : namespace Envoy {
25 : namespace {
26 69 : std::vector<std::string> toArgsVector(int argc, const char* const* argv) {
27 69 : std::vector<std::string> args;
28 69 : args.reserve(argc);
29 :
30 138 : for (int i = 0; i < argc; ++i) {
31 69 : args.emplace_back(argv[i]);
32 69 : }
33 69 : return args;
34 69 : }
35 :
36 : } // namespace
37 :
38 : OptionsImpl::OptionsImpl(int argc, const char* const* argv,
39 : const HotRestartVersionCb& hot_restart_version_cb,
40 : spdlog::level::level_enum default_log_level)
41 69 : : OptionsImpl(toArgsVector(argc, argv), hot_restart_version_cb, default_log_level) {}
42 :
43 : OptionsImpl::OptionsImpl(std::vector<std::string> args,
44 : const HotRestartVersionCb& hot_restart_version_cb,
45 69 : spdlog::level::level_enum default_log_level) {
46 69 : std::string log_levels_string = fmt::format("Log levels: {}", allowedLogLevels());
47 69 : log_levels_string +=
48 69 : fmt::format("\nDefault is [{}]", spdlog::level::level_string_views[default_log_level]);
49 :
50 69 : const std::string component_log_level_string =
51 69 : "Comma-separated list of component log levels. For example upstream:debug,config:trace";
52 69 : const std::string log_format_string =
53 69 : fmt::format("Log message format in spdlog syntax "
54 69 : "(see https://github.com/gabime/spdlog/wiki/3.-Custom-formatting)"
55 69 : "\nDefault is \"{}\"",
56 69 : Logger::Logger::DEFAULT_LOG_FORMAT);
57 :
58 : // NOLINTNEXTLINE(clang-analyzer-optin.cplusplus.VirtualCall)
59 69 : TCLAP::CmdLine cmd("envoy", ' ', VersionInfo::version());
60 69 : TCLAP::ValueArg<uint32_t> base_id(
61 69 : "", "base-id", "Base ID so that multiple envoys can run on the same host if needed", false, 0,
62 69 : "uint32_t", cmd);
63 69 : TCLAP::SwitchArg use_dynamic_base_id(
64 69 : "", "use-dynamic-base-id",
65 69 : "The server chooses a base ID dynamically. Supersedes a static base ID. May not be used "
66 69 : "when the restart epoch is non-zero.",
67 69 : cmd, false);
68 69 : TCLAP::ValueArg<std::string> base_id_path(
69 69 : "", "base-id-path", "Path to which the base ID is written", false, "", "string", cmd);
70 69 : TCLAP::ValueArg<uint32_t> concurrency("", "concurrency", "# of worker threads to run", false,
71 69 : std::thread::hardware_concurrency(), "uint32_t", cmd);
72 69 : TCLAP::ValueArg<std::string> config_path("c", "config-path", "Path to configuration file", false,
73 69 : "", "string", cmd);
74 69 : TCLAP::ValueArg<std::string> config_yaml(
75 69 : "", "config-yaml", "Inline YAML configuration, merges with the contents of --config-path",
76 69 : false, "", "string", cmd);
77 :
78 69 : TCLAP::SwitchArg allow_unknown_fields("", "allow-unknown-fields",
79 69 : "Allow unknown fields in static configuration (DEPRECATED)",
80 69 : cmd, false);
81 69 : TCLAP::SwitchArg allow_unknown_static_fields("", "allow-unknown-static-fields",
82 69 : "Allow unknown fields in static configuration", cmd,
83 69 : false);
84 69 : TCLAP::SwitchArg reject_unknown_dynamic_fields("", "reject-unknown-dynamic-fields",
85 69 : "Reject unknown fields in dynamic configuration",
86 69 : cmd, false);
87 69 : TCLAP::SwitchArg ignore_unknown_dynamic_fields("", "ignore-unknown-dynamic-fields",
88 69 : "Ignore unknown fields in dynamic configuration",
89 69 : cmd, false);
90 :
91 69 : TCLAP::ValueArg<std::string> admin_address_path("", "admin-address-path", "Admin address path",
92 69 : false, "", "string", cmd);
93 69 : TCLAP::ValueArg<std::string> local_address_ip_version("", "local-address-ip-version",
94 69 : "The local "
95 69 : "IP address version (v4 or v6).",
96 69 : false, "v4", "string", cmd);
97 69 : TCLAP::ValueArg<std::string> log_level(
98 69 : "l", "log-level", log_levels_string, false,
99 69 : spdlog::level::level_string_views[default_log_level].data(), "string", cmd);
100 69 : TCLAP::ValueArg<std::string> component_log_level(
101 69 : "", "component-log-level", component_log_level_string, false, "", "string", cmd);
102 69 : TCLAP::ValueArg<std::string> log_format("", "log-format", log_format_string, false,
103 69 : Logger::Logger::DEFAULT_LOG_FORMAT, "string", cmd);
104 69 : TCLAP::SwitchArg log_format_escaped("", "log-format-escaped",
105 69 : "Escape c-style escape sequences in the application logs",
106 69 : cmd, false);
107 69 : TCLAP::SwitchArg enable_fine_grain_logging(
108 69 : "", "enable-fine-grain-logging",
109 69 : "Logger mode: enable file level log control (Fine-Grain Logger) or not", cmd, false);
110 69 : TCLAP::ValueArg<std::string> log_path("", "log-path", "Path to logfile", false, "", "string",
111 69 : cmd);
112 69 : TCLAP::ValueArg<uint32_t> restart_epoch("", "restart-epoch", "Hot restart epoch #", false, 0,
113 69 : "uint32_t", cmd);
114 69 : TCLAP::SwitchArg hot_restart_version_option("", "hot-restart-version",
115 69 : "Hot restart compatibility version", cmd);
116 69 : TCLAP::ValueArg<std::string> service_cluster("", "service-cluster", "Cluster name", false, "",
117 69 : "string", cmd);
118 69 : TCLAP::ValueArg<std::string> service_node("", "service-node", "Node name", false, "", "string",
119 69 : cmd);
120 69 : TCLAP::ValueArg<std::string> service_zone("", "service-zone", "Zone name", false, "", "string",
121 69 : cmd);
122 69 : TCLAP::ValueArg<uint32_t> file_flush_interval_msec("", "file-flush-interval-msec",
123 69 : "Interval for log flushing in msec", false,
124 69 : 10000, "uint32_t", cmd);
125 69 : TCLAP::ValueArg<uint32_t> drain_time_s("", "drain-time-s",
126 69 : "Hot restart and LDS removal drain time in seconds", false,
127 69 : 600, "uint32_t", cmd);
128 69 : TCLAP::ValueArg<std::string> drain_strategy(
129 69 : "", "drain-strategy",
130 69 : "Hot restart drain sequence behaviour, one of 'gradual' (default) or 'immediate'.", false,
131 69 : "gradual", "string", cmd);
132 69 : TCLAP::ValueArg<uint32_t> parent_shutdown_time_s("", "parent-shutdown-time-s",
133 69 : "Hot restart parent shutdown time in seconds",
134 69 : false, 900, "uint32_t", cmd);
135 69 : TCLAP::ValueArg<std::string> mode("", "mode",
136 69 : "One of 'serve' (default; validate configs and then serve "
137 69 : "traffic normally) or 'validate' (validate configs and exit).",
138 69 : false, "serve", "string", cmd);
139 69 : TCLAP::SwitchArg disable_hot_restart("", "disable-hot-restart",
140 69 : "Disable hot restart functionality", cmd, false);
141 69 : TCLAP::SwitchArg enable_mutex_tracing(
142 69 : "", "enable-mutex-tracing", "Enable mutex contention tracing functionality", cmd, false);
143 69 : TCLAP::SwitchArg cpuset_threads(
144 69 : "", "cpuset-threads", "Get the default # of worker threads from cpuset size", cmd, false);
145 :
146 69 : TCLAP::ValueArg<std::string> disable_extensions("", "disable-extensions",
147 69 : "Comma-separated list of extensions to disable",
148 69 : false, "", "string", cmd);
149 :
150 69 : TCLAP::ValueArg<std::string> socket_path("", "socket-path", "Path to hot restart socket file",
151 69 : false, "@envoy_domain_socket", "string", cmd);
152 :
153 69 : TCLAP::ValueArg<std::string> socket_mode("", "socket-mode", "Socket file permission", false,
154 69 : "600", "string", cmd);
155 69 : TCLAP::SwitchArg enable_core_dump("", "enable-core-dump", "Enable core dumps", cmd, false);
156 :
157 69 : TCLAP::MultiArg<std::string> stats_tag(
158 69 : "", "stats-tag",
159 69 : "This flag provides a universal tag for all stats generated by Envoy. The format is "
160 69 : "``tag:value``. Only alphanumeric values are allowed for tag names. For tag values all "
161 69 : "characters are permitted except for '.' (dot). This flag can be repeated multiple times to "
162 69 : "set multiple universal tags. Multiple values for the same tag name are not allowed.",
163 69 : false, "string", cmd);
164 :
165 69 : cmd.setExceptionHandling(false);
166 :
167 69 : std::function failure_function = [&](TCLAP::ArgException& e) {
168 0 : TRY_ASSERT_MAIN_THREAD { cmd.getOutput()->failure(cmd, e); }
169 0 : END_TRY
170 0 : CATCH(const TCLAP::ExitException&, {
171 : // failure() has already written an informative message to stderr, so all that's left to do
172 : // is throw our own exception with the original message.
173 0 : throw MalformedArgvException(e.what());
174 0 : });
175 0 : };
176 :
177 69 : TRY_ASSERT_MAIN_THREAD {
178 69 : cmd.parse(args);
179 69 : count_ = cmd.getArgList().size();
180 69 : }
181 69 : END_TRY
182 69 : MULTI_CATCH(
183 69 : TCLAP::ArgException & e, { failure_function(e); },
184 69 : { throw NoServingException("NoServingException"); });
185 :
186 69 : hot_restart_disabled_ = disable_hot_restart.getValue();
187 69 : mutex_tracing_enabled_ = enable_mutex_tracing.getValue();
188 69 : core_dump_enabled_ = enable_core_dump.getValue();
189 :
190 69 : cpuset_threads_ = cpuset_threads.getValue();
191 :
192 69 : if (log_level.isSet()) {
193 0 : auto status_or_error = parseAndValidateLogLevel(log_level.getValue());
194 0 : if (!status_or_error.status().ok()) {
195 0 : logError(std::string(status_or_error.status().message()));
196 0 : }
197 0 : log_level_ = status_or_error.value();
198 69 : } else {
199 69 : log_level_ = default_log_level;
200 69 : }
201 :
202 69 : log_format_ = log_format.getValue();
203 69 : log_format_set_ = log_format.isSet();
204 69 : log_format_escaped_ = log_format_escaped.getValue();
205 69 : enable_fine_grain_logging_ = enable_fine_grain_logging.getValue();
206 :
207 69 : parseComponentLogLevels(component_log_level.getValue());
208 :
209 69 : if (mode.getValue() == "serve") {
210 69 : mode_ = Server::Mode::Serve;
211 69 : } else if (mode.getValue() == "validate") {
212 0 : mode_ = Server::Mode::Validate;
213 0 : } else if (mode.getValue() == "init_only") {
214 0 : mode_ = Server::Mode::InitOnly;
215 0 : } else {
216 0 : const std::string message = fmt::format("error: unknown mode '{}'", mode.getValue());
217 0 : throw MalformedArgvException(message);
218 0 : }
219 :
220 69 : if (local_address_ip_version.getValue() == "v4") {
221 69 : local_address_ip_version_ = Network::Address::IpVersion::v4;
222 69 : } else if (local_address_ip_version.getValue() == "v6") {
223 0 : local_address_ip_version_ = Network::Address::IpVersion::v6;
224 0 : } else {
225 0 : const std::string message =
226 0 : fmt::format("error: unknown IP address version '{}'", local_address_ip_version.getValue());
227 0 : throw MalformedArgvException(message);
228 0 : }
229 69 : base_id_ = base_id.getValue();
230 69 : use_dynamic_base_id_ = use_dynamic_base_id.getValue();
231 69 : base_id_path_ = base_id_path.getValue();
232 69 : restart_epoch_ = restart_epoch.getValue();
233 :
234 69 : if (use_dynamic_base_id_ && restart_epoch_ > 0) {
235 0 : const std::string message = fmt::format(
236 0 : "error: cannot use --restart-epoch={} with --use-dynamic-base-id", restart_epoch_);
237 0 : throw MalformedArgvException(message);
238 0 : }
239 :
240 69 : if (!concurrency.isSet() && cpuset_threads_) {
241 : // The 'concurrency' command line option wasn't set but the 'cpuset-threads'
242 : // option was set. Use the number of CPUs assigned to the process cpuset, if
243 : // that can be known.
244 0 : concurrency_ = OptionsImplPlatform::getCpuCount();
245 69 : } else {
246 69 : if (concurrency.isSet() && cpuset_threads_ && cpuset_threads.isSet()) {
247 0 : ENVOY_LOG(warn, "Both --concurrency and --cpuset-threads options are set; not applying "
248 0 : "--cpuset-threads.");
249 0 : }
250 69 : concurrency_ = std::max(1U, concurrency.getValue());
251 69 : }
252 :
253 69 : config_path_ = config_path.getValue();
254 69 : config_yaml_ = config_yaml.getValue();
255 69 : if (allow_unknown_fields.getValue()) {
256 0 : ENVOY_LOG(warn,
257 0 : "--allow-unknown-fields is deprecated, use --allow-unknown-static-fields instead.");
258 0 : }
259 69 : allow_unknown_static_fields_ =
260 69 : allow_unknown_static_fields.getValue() || allow_unknown_fields.getValue();
261 69 : reject_unknown_dynamic_fields_ = reject_unknown_dynamic_fields.getValue();
262 69 : ignore_unknown_dynamic_fields_ = ignore_unknown_dynamic_fields.getValue();
263 69 : admin_address_path_ = admin_address_path.getValue();
264 69 : log_path_ = log_path.getValue();
265 69 : service_cluster_ = service_cluster.getValue();
266 69 : service_node_ = service_node.getValue();
267 69 : service_zone_ = service_zone.getValue();
268 69 : file_flush_interval_msec_ = std::chrono::milliseconds(file_flush_interval_msec.getValue());
269 69 : drain_time_ = std::chrono::seconds(drain_time_s.getValue());
270 69 : parent_shutdown_time_ = std::chrono::seconds(parent_shutdown_time_s.getValue());
271 69 : socket_path_ = socket_path.getValue();
272 :
273 69 : if (socket_path_.at(0) == '@') {
274 69 : socket_mode_ = 0;
275 69 : } else {
276 0 : uint64_t socket_mode_helper;
277 0 : if (!StringUtil::atoull(socket_mode.getValue().c_str(), socket_mode_helper, 8)) {
278 0 : throw MalformedArgvException(
279 0 : fmt::format("error: invalid socket-mode '{}'", socket_mode.getValue()));
280 0 : }
281 0 : socket_mode_ = socket_mode_helper;
282 0 : }
283 :
284 69 : if (drain_strategy.getValue() == "immediate") {
285 0 : drain_strategy_ = Server::DrainStrategy::Immediate;
286 69 : } else if (drain_strategy.getValue() == "gradual") {
287 69 : drain_strategy_ = Server::DrainStrategy::Gradual;
288 69 : } else {
289 0 : throw MalformedArgvException(
290 0 : fmt::format("error: unknown drain-strategy '{}'", mode.getValue()));
291 0 : }
292 :
293 69 : if (hot_restart_version_option.getValue()) {
294 0 : std::cerr << hot_restart_version_cb(!hot_restart_disabled_);
295 0 : throw NoServingException("NoServingException");
296 0 : }
297 :
298 69 : if (!disable_extensions.getValue().empty()) {
299 0 : disabled_extensions_ = absl::StrSplit(disable_extensions.getValue(), ',');
300 0 : }
301 :
302 69 : if (!stats_tag.getValue().empty()) {
303 0 : for (const auto& cli_tag_pair : stats_tag.getValue()) {
304 :
305 0 : std::vector<absl::string_view> cli_tag_pair_tokens =
306 0 : absl::StrSplit(cli_tag_pair, absl::MaxSplits(':', 1));
307 0 : if (cli_tag_pair_tokens.size() != 2) {
308 0 : throw MalformedArgvException(
309 0 : fmt::format("error: misformatted stats-tag '{}'", cli_tag_pair));
310 0 : }
311 :
312 0 : auto name = cli_tag_pair_tokens[0];
313 0 : if (!Stats::TagUtility::isTagNameValid(name)) {
314 0 : throw MalformedArgvException(
315 0 : fmt::format("error: misformatted stats-tag '{}' contains invalid char in '{}'",
316 0 : cli_tag_pair, name));
317 0 : }
318 :
319 0 : auto value = cli_tag_pair_tokens[1];
320 0 : if (!Stats::TagUtility::isTagValueValid(value)) {
321 0 : throw MalformedArgvException(
322 0 : fmt::format("error: misformatted stats-tag '{}' contains invalid char in '{}'",
323 0 : cli_tag_pair, value));
324 0 : }
325 :
326 0 : stats_tags_.emplace_back(Stats::Tag{std::string(name), std::string(value)});
327 0 : }
328 0 : }
329 69 : }
330 :
331 69 : std::string OptionsImpl::allowedLogLevels() {
332 69 : std::string allowed_log_levels;
333 483 : for (auto level_string_view : spdlog::level::level_string_views) {
334 483 : if (level_string_view == spdlog::level::to_string_view(spdlog::level::warn)) {
335 69 : allowed_log_levels += fmt::format("[{}|warn]", level_string_view);
336 414 : } else {
337 414 : allowed_log_levels += fmt::format("[{}]", level_string_view);
338 414 : }
339 483 : }
340 69 : return allowed_log_levels;
341 69 : }
342 :
343 69 : void OptionsImpl::parseComponentLogLevels(const std::string& component_log_levels) {
344 69 : if (component_log_levels.empty()) {
345 69 : return;
346 69 : }
347 0 : component_log_level_str_ = component_log_levels;
348 0 : std::vector<std::string> log_levels = absl::StrSplit(component_log_levels, ',');
349 0 : for (auto& level : log_levels) {
350 0 : std::vector<std::string> log_name_level = absl::StrSplit(level, ':');
351 0 : if (log_name_level.size() != 2) {
352 0 : logError(fmt::format("error: component log level not correctly specified '{}'", level));
353 0 : }
354 0 : std::string log_name = log_name_level[0];
355 0 : auto status_or_error = parseAndValidateLogLevel(log_name_level[1]);
356 0 : if (!status_or_error.status().ok()) {
357 0 : logError(std::string(status_or_error.status().message()));
358 0 : }
359 0 : spdlog::level::level_enum log_level = status_or_error.value();
360 0 : Logger::Logger* logger_to_change = Logger::Registry::logger(log_name);
361 0 : if (!logger_to_change) {
362 0 : logError(fmt::format("error: invalid component specified '{}'", log_name));
363 0 : }
364 0 : component_log_levels_.push_back(std::make_pair(log_name, log_level));
365 0 : }
366 0 : }
367 :
368 0 : void OptionsImpl::logError(const std::string& error) { throw MalformedArgvException(error); }
369 :
370 0 : Server::CommandLineOptionsPtr OptionsImpl::toCommandLineOptions() const {
371 0 : Server::CommandLineOptionsPtr command_line_options =
372 0 : std::make_unique<envoy::admin::v3::CommandLineOptions>();
373 0 : command_line_options->set_base_id(baseId());
374 0 : command_line_options->set_use_dynamic_base_id(useDynamicBaseId());
375 0 : command_line_options->set_base_id_path(baseIdPath());
376 0 : command_line_options->set_concurrency(concurrency());
377 0 : command_line_options->set_config_path(configPath());
378 0 : command_line_options->set_config_yaml(configYaml());
379 0 : command_line_options->set_allow_unknown_static_fields(allow_unknown_static_fields_);
380 0 : command_line_options->set_reject_unknown_dynamic_fields(reject_unknown_dynamic_fields_);
381 0 : command_line_options->set_ignore_unknown_dynamic_fields(ignore_unknown_dynamic_fields_);
382 0 : command_line_options->set_admin_address_path(adminAddressPath());
383 0 : command_line_options->set_component_log_level(component_log_level_str_);
384 0 : command_line_options->set_log_level(spdlog::level::to_string_view(logLevel()).data(),
385 0 : spdlog::level::to_string_view(logLevel()).size());
386 0 : command_line_options->set_log_format(logFormat());
387 0 : command_line_options->set_log_format_escaped(logFormatEscaped());
388 0 : command_line_options->set_enable_fine_grain_logging(enableFineGrainLogging());
389 0 : command_line_options->set_log_path(logPath());
390 0 : command_line_options->set_service_cluster(serviceClusterName());
391 0 : command_line_options->set_service_node(serviceNodeName());
392 0 : command_line_options->set_service_zone(serviceZone());
393 0 : if (mode() == Server::Mode::Serve) {
394 0 : command_line_options->set_mode(envoy::admin::v3::CommandLineOptions::Serve);
395 0 : } else if (mode() == Server::Mode::Validate) {
396 0 : command_line_options->set_mode(envoy::admin::v3::CommandLineOptions::Validate);
397 0 : } else {
398 0 : command_line_options->set_mode(envoy::admin::v3::CommandLineOptions::InitOnly);
399 0 : }
400 0 : if (localAddressIpVersion() == Network::Address::IpVersion::v4) {
401 0 : command_line_options->set_local_address_ip_version(envoy::admin::v3::CommandLineOptions::v4);
402 0 : } else {
403 0 : command_line_options->set_local_address_ip_version(envoy::admin::v3::CommandLineOptions::v6);
404 0 : }
405 0 : command_line_options->mutable_file_flush_interval()->MergeFrom(
406 0 : Protobuf::util::TimeUtil::MillisecondsToDuration(fileFlushIntervalMsec().count()));
407 :
408 0 : command_line_options->mutable_drain_time()->MergeFrom(
409 0 : Protobuf::util::TimeUtil::SecondsToDuration(drainTime().count()));
410 0 : command_line_options->set_drain_strategy(drainStrategy() == Server::DrainStrategy::Immediate
411 0 : ? envoy::admin::v3::CommandLineOptions::Immediate
412 0 : : envoy::admin::v3::CommandLineOptions::Gradual);
413 0 : command_line_options->mutable_parent_shutdown_time()->MergeFrom(
414 0 : Protobuf::util::TimeUtil::SecondsToDuration(parentShutdownTime().count()));
415 :
416 0 : command_line_options->set_disable_hot_restart(hotRestartDisabled());
417 0 : command_line_options->set_enable_mutex_tracing(mutexTracingEnabled());
418 0 : command_line_options->set_cpuset_threads(cpusetThreadsEnabled());
419 0 : command_line_options->set_restart_epoch(restartEpoch());
420 0 : for (const auto& e : disabledExtensions()) {
421 0 : command_line_options->add_disabled_extensions(e);
422 0 : }
423 0 : command_line_options->set_socket_path(socketPath());
424 0 : command_line_options->set_socket_mode(socketMode());
425 0 : for (const auto& tag : statsTags()) {
426 0 : command_line_options->add_stats_tag(fmt::format("{}:{}", tag.name_, tag.value_));
427 0 : }
428 0 : return command_line_options;
429 0 : }
430 :
431 : OptionsImpl::OptionsImpl(const std::string& service_cluster, const std::string& service_node,
432 98 : const std::string& service_zone, spdlog::level::level_enum log_level) {
433 98 : setLogLevel(log_level);
434 98 : setServiceClusterName(service_cluster);
435 98 : setServiceNodeName(service_node);
436 98 : setServiceZone(service_zone);
437 98 : }
438 :
439 : } // namespace Envoy
|