/* * Copyright (C) 2017-present ScyllaDB * */ /* * SPDX-License-Identifier: LicenseRef-ScyllaDB-Source-Available-1.1 */ #pragma once #include #include #include #include #include #include #include "utils/updateable_value.hh" #include "seastarx.hh" namespace seastar { class file; } namespace seastar::json { class json_return_type; } namespace YAML { class Node; } namespace boost::program_options { class options_description; class options_description_easy_init; } namespace utils { namespace bpo = boost::program_options; class config_type { std::string_view _name; std::function _to_json; private: template std::function make_to_json(json::json_return_type (*func)(const NativeType&)) { return [func] (const void* value) { return func(*static_cast(value)); }; } public: template config_type(std::string_view name, json::json_return_type (*to_json)(const NativeType&)) : _name(name), _to_json(make_to_json(to_json)) {} std::string_view name() const { return _name; } json::json_return_type to_json(const void* value) const; }; template extern const config_type& config_type_for(); class config_file { static thread_local unsigned s_shard_id; struct any_value { virtual ~any_value() = default; virtual std::unique_ptr clone() const = 0; virtual void update_from(const any_value* source) = 0; }; std::vector>> _per_shard_values { 1 }; public: typedef std::unordered_map string_map; typedef std::vector string_list; enum class value_status { Used, ///< a valid option which changes scylla's behavior. only the ///< "Used" options are added to the command line options, ///< and can be specified with command line. Unused, ///< an option inherited or not yet implemented. ///< We want to minimize their "visibility" from user. So ///< despite that they are still accepted in the config file, ///< they are not considered as valid options if specified ///< using the command line anymore. ///< initially, we had loads of these options from Cassandra. ///< they are also used for options supported by the older ///< versions of Scylla. ///< (In the past used to deprecate an option, ///< now there's a separate Deprecated enumerator for that purpose) Invalid, ///< an option inherited, but we don't intend to ///< implement it. on the contrary, we print a warning message ///< at seeing each of these options if specified when booting ///< up. these options are kept around only to ease the ///< migration process so user can keep using their existing ///< settings. ///< (In the past used to deprecate an option, ///< now there's a separate Deprecated enumerator for that purpose) Deprecated, ///< an option that was used once, but became deprecated. ///< If specified in configuration file or as a cmd line param, ///< we'll parse it and print a warning saying it's deprecated. ///< You may expect such option will become Unused/Invalid ///< or will be removed altogether some day. }; enum class liveness { LiveUpdate, MustRestart, }; enum class config_source : uint8_t { None, SettingsFile, CommandLine, CQL, Internal, API, }; struct config_src { config_file* _cf; std::string_view _name, _alias, _desc; const config_type* _type; size_t _per_shard_values_offset; protected: virtual const void* current_value() const = 0; public: config_src(config_file* cf, std::string_view name, const config_type* type, std::string_view desc) : _cf(cf) , _name(name) , _desc(desc) , _type(type) {} config_src(config_file* cf, std::string_view name, std::string_view alias, const config_type* type, std::string_view desc) : _cf(cf) , _name(name) , _alias(alias) , _desc(desc) , _type(type) {} virtual ~config_src() {} const std::string_view & name() const { return _name; } std::string_view alias() const { return _alias; } const std::string_view & desc() const { return _desc; } std::string_view type_name() const { return _type->name(); } config_file * get_config_file() const { return _cf; } bool matches(std::string_view name) const; virtual void add_command_line_option(bpo::options_description_easy_init&) = 0; virtual void set_value(const YAML::Node&, config_source) = 0; virtual bool set_value(sstring, config_source) = 0; virtual future set_value_on_all_shards(sstring, config_source) = 0; virtual value_status status() const noexcept = 0; virtual config_source source() const noexcept = 0; sstring source_name() const noexcept; json::json_return_type value_as_json() const; }; template struct named_value : public config_src { private: friend class config; config_source _source = config_source::None; value_status _value_status; struct the_value_type final : any_value { the_value_type(T value); utils::updateable_value_source value; std::unique_ptr clone() const override; void update_from(const any_value* source) override; }; liveness _liveness; std::vector _allowed_values; protected: updateable_value_source& the_value(); const updateable_value_source& the_value() const; const void* current_value() const override; public: typedef T type; typedef named_value MyType; named_value(config_file* file, std::string_view name, std::string_view alias, liveness liveness_, value_status vs, const T& t = T(), std::string_view desc = {}, std::initializer_list allowed_values = {}); named_value(config_file* file, std::string_view name, liveness liveness_, value_status vs, const T& t = T(), std::string_view desc = {}, std::initializer_list allowed_values = {}); named_value(config_file* file, std::string_view name, std::string_view alias, value_status vs, const T& t = T(), std::string_view desc = {}, std::initializer_list allowed_values = {}); named_value(config_file* file, std::string_view name, value_status vs, const T& t = T(), std::string_view desc = {}, std::initializer_list allowed_values = {}); value_status status() const noexcept override; config_source source() const noexcept override; bool is_set() const; MyType & operator()(const T& t, config_source src = config_source::Internal); MyType & operator()(T&& t, config_source src = config_source::Internal); void set(T&& t, config_source src = config_source::None); const T& operator()() const; operator updateable_value() const &; observer observe(std::function callback) const; void add_command_line_option(bpo::options_description_easy_init&) override; void set_value(const YAML::Node&, config_source) override; bool set_value(sstring, config_source) override; // For setting a single value on all shards, // without having to call broadcast_to_all_shards // that broadcasts all values to all shards. future set_value_on_all_shards(sstring, config_source) override; }; typedef std::reference_wrapper cfg_ref; config_file(std::initializer_list = {}); config_file(const config_file&) = delete; virtual ~config_file() = default; void add(cfg_ref, std::unique_ptr value); void add(std::initializer_list); void add(const std::vector &); boost::program_options::options_description get_options_description(); boost::program_options::options_description get_options_description(boost::program_options::options_description); boost::program_options::options_description_easy_init& add_options(boost::program_options::options_description_easy_init&); boost::program_options::options_description_easy_init& add_deprecated_options(boost::program_options::options_description_easy_init&); /** * Default behaviour for yaml parser is to throw on * unknown stuff, invalid opts or conversion errors. * * Error handling function allows overriding this. * * error: