ipvs_t::ipvs_t(context_t& context, const std::string& name, const dynamic_t& args): category_type(context, name, args), m_context(context), m_log(new logging::log_t(context, name)), m_default_scheduler(args.as_object().at("scheduler", "wlc").to<std::string>()), m_default_weight(args.as_object().at("weight", 1).to<unsigned int>()) { if(::ipvs_init() != 0) { throw std::system_error(errno, ipvs_category(), "unable to initialize IPVS"); } COCAINE_LOG_INFO(m_log, "using IPVS version %d", ::ipvs_version()); if(args["port-range"].empty()) { throw cocaine::error_t("no port ranges have been specified"); } uint16_t min = args["port-range"][0].asUInt(), max = args["port-range"][1].asUInt(); COCAINE_LOG_INFO(m_log, "%u gateway ports available, %u through %u", max - min, min, max); while(min != max) { m_ports.push(--max); } ::ipvs_flush(); }
static result_type convert(const dynamic_t& from) { return cocaine::config_t::component_t { from.as_object().at("type", "unspecified").as_string(), from.as_object().at("args", dynamic_t::empty_object) }; }
multicast_t::multicast_t(context_t& context, interface& locator, const std::string& name, const dynamic_t& args): category_type(context, locator, name, args), m_context(context), m_log(context.log(name)), m_locator(locator), m_cfg(args.to<multicast_cfg_t>()), m_socket(locator.asio()), m_timer(locator.asio()) { m_socket.open(m_cfg.endpoint.protocol()); m_socket.set_option(socket_base::reuse_address(true)); if(m_cfg.endpoint.address().is_v4()) { m_socket.bind(udp::endpoint(address_v4::any(), m_cfg.endpoint.port())); } else { m_socket.bind(udp::endpoint(address_v6::any(), m_cfg.endpoint.port())); } if(args.as_object().count("interface")) { auto interface = args.as_object().at("interface"); if(m_cfg.endpoint.address().is_v4()) { m_socket.set_option(multicast::outbound_interface(interface.to<ip::address>().to_v4())); } else { m_socket.set_option(multicast::outbound_interface(interface.as_uint())); } } m_socket.set_option(multicast::enable_loopback(args.as_object().at("loopback", false).as_bool())); m_socket.set_option(multicast::hops(args.as_object().at("hops", 1u).as_uint())); COCAINE_LOG_INFO(m_log, "joining multicast group '%s'", m_cfg.endpoint)( "uuid", m_locator.uuid() ); m_socket.set_option(multicast::join_group(m_cfg.endpoint.address())); const auto announce = std::make_shared<announce_t>(); m_socket.async_receive_from(buffer(announce->buffer.data(), announce->buffer.size()), announce->endpoint, std::bind(&multicast_t::on_receive, this, ph::_1, ph::_2, announce) ); m_signals = std::make_shared<dispatch<context_tag>>(name); m_signals->on<context::prepared>(std::bind(&multicast_t::on_publish, this, std::error_code())); context.listen(m_signals, m_locator.asio()); }
process_t::process_t(context_t& context, const std::string& name, const dynamic_t& args): category_type(context, name, args), m_context(context), m_log(context.log(name)), m_name(name), m_working_directory(fs::path(args.as_object().at("spool", "/var/spool/cocaine").as_string()) / name) { #ifdef COCAINE_ALLOW_CGROUPS int rv = 0; if((rv = cgroup_init()) != 0) { throw std::system_error(rv, cgroup_category(), "unable to initialize cgroups"); } m_cgroup = cgroup_new_cgroup(m_name.c_str()); // NOTE: Looks like if this is not done, then libcgroup will chown everything as root. cgroup_set_uid_gid(m_cgroup, getuid(), getgid(), getuid(), getgid()); for(auto type = args.as_object().begin(); type != args.as_object().end(); ++type) { if(!type->second.is_object() || type->second.as_object().empty()) { continue; } cgroup_controller* ptr = cgroup_add_controller(m_cgroup, type->first.c_str()); for(auto it = type->second.as_object().begin(); it != type->second.as_object().end(); ++it) { COCAINE_LOG_INFO(m_log, "setting cgroup controller '%s' parameter '%s' to '%s'", type->first, it->first, boost::lexical_cast<std::string>(it->second) ); try { it->second.apply(cgroup_configurator_t(ptr, it->first.c_str())); } catch(const cocaine::error_t& e) { COCAINE_LOG_ERROR(m_log, "unable to set cgroup controller '%s' parameter '%s' - %s", type->first, it->first, e.what() ); } } } if((rv = cgroup_create_cgroup(m_cgroup, false)) != 0) { cgroup_free(&m_cgroup); throw std::system_error(rv, cgroup_category(), "unable to create cgroup"); } #endif }
cache_t::cache_t(context_t& context, io::reactor_t& reactor, const std::string& name, const dynamic_t& args): service_t(context, reactor, name, args), implements<io::cache_tag>(name), cache_(args.as_object().at("max-size", 1000000).to<size_t>()) { on<io::cache::get>(std::bind(&cache_t::get, this, _1)); on<io::cache::put>(std::bind(&cache_t::put, this, _1, _2)); }
darkmetrics_t::darkmetrics_t(context_t& context, peers_t& peers, const dynamic_t& args) : unicorn_name_(args.as_object().at("unicorn", dynamic_t::empty_string).as_string()) , unicorn_prefix_(args.as_object().at("unicorn_prefix", dynamic_t::empty_string).as_string()) , unicorn_retry_after_(args.as_object().at("unicorn_retry_after_s", 20U).as_uint()) , enabled_(args.as_object().at("enabled", false).as_bool()) , ttl_(args.as_object().at("ttl_s", 300U).as_uint()) , logger_(context.log("vicodyn/darkmetrics")) , peers_(peers) { if (enabled_) { if (unicorn_name_.empty() || unicorn_prefix_.empty()) { throw error_t("invalid configuration of darkmetrics: `unicorn` and `unicorn_prefix` are required"); } unicorn_ = api::unicorn(context, unicorn_name_); } COCAINE_LOG_INFO(logger_, "balancing by system weights is {}", enabled_ ? "on" : "off"); }
static result_type convert(const dynamic_t& source) { result_type result; try { result.endpoint = udp::endpoint( source.as_object().at("group").to<ip::address>(), source.as_object().at("port", 10053u).as_uint() ); } catch(std::out_of_range& e) { throw cocaine::error_t("no multicast group has been specified"); } result.interval = boost::posix_time::seconds( source.as_object().at("interval", 5u).as_uint() ); return result; }
storage_t::storage_t(context_t& context, reactor_t& reactor, const std::string& name, const dynamic_t& args): api::service_t(context, reactor, name, args), implements<io::storage_tag>(context, name) { auto storage = api::storage(context, args.as_object().at("backend", "core").as_string()); on<io::storage::read>(std::bind(&api::storage_t::read, storage, _1, _2)); on<io::storage::write>(std::bind(&api::storage_t::write, storage, _1, _2, _3, _4)); on<io::storage::remove>(std::bind(&api::storage_t::remove, storage, _1, _2)); on<io::storage::find>(std::bind(&api::storage_t::find, storage, _1, _2)); }
files_t::files_t(const config_t& config, const dynamic_t& args): category_type(config, args), m_file(nullptr) { const std::string path = args.as_object()["path"].as_string(); m_file = std::fopen(path.c_str(), "a"); if(m_file == nullptr) { throw std::system_error(errno, std::system_category(), cocaine::format("unable to open '%s'", path)); } }
elliptics_service_t::elliptics_service_t(context_t &context, asio::io_service &reactor, const std::string &name, const dynamic_t &args) : api::service_t(context, reactor, name, args), dispatch<io::elliptics_tag>(name), m_storage(api::storage(context, args.as_object().at("source", "core").as_string())), m_elliptics(dynamic_cast<storage::elliptics_storage_t*>(m_storage.get())) { debug() << m_elliptics << std::endl; if (!m_elliptics) { throw std::system_error(-1, std::generic_category(), "to be able to use elliptics service, storage must be also elliptics"); } on<io::storage::read >(std::bind(&elliptics_service_t::read, this, ph::_1, ph::_2)); on<io::storage::write >(std::bind(&elliptics_service_t::write, this, ph::_1, ph::_2, ph::_3, ph::_4)); on<io::storage::remove>(std::bind(&elliptics_service_t::remove, this, ph::_1, ph::_2)); on<io::storage::find >(std::bind(&elliptics_service_t::find, this, ph::_1, ph::_2)); on<io::elliptics::cache_read >(std::bind(&elliptics_service_t::cache_read, this, ph::_1, ph::_2)); on<io::elliptics::cache_write>(std::bind(&elliptics_service_t::cache_write, this, ph::_1, ph::_2, ph::_3, ph::_4)); on<io::elliptics::bulk_read >(std::bind(&elliptics_service_t::bulk_read, this, ph::_1, ph::_2)); on<io::elliptics::read_latest>(std::bind(&elliptics_service_t::read_latest, this, ph::_1, ph::_2)); }
static result_type convert(const dynamic_t& from) { result_type component; const auto& logging = from.as_object(); for(auto it = logging.begin(); it != logging.end(); ++it) { using namespace blackhole::repository; auto object = it->second.as_object(); auto loggers = object.at("loggers", dynamic_t::empty_array); config_t::logging_t::logger_t log { logmask(object.at("verbosity", defaults::log_verbosity).as_string()), object.at("timestamp", defaults::log_timestamp).as_string(), config::parser_t<dynamic_t, blackhole::log_config_t>::parse(it->first, loggers) }; component.loggers[it->first] = log; } return component; }
mongo_storage_t::mongo_storage_t(context_t& context, const std::string& name, const dynamic_t& args): category_type(context, name, args), m_log(context.log(name)), m_client(connect(args.as_object().at("uri", "<unspecified>").to<std::string>())) { }
control_service_t::control_service_t(context_t& context, io::reactor_t& reactor, const std::string& name, const dynamic_t& args): api::service_t(context, reactor, name, args), dispatch<io::raft_control_tag<msgpack::object, msgpack::object>>(name), m_context(context), m_log(context.log(name)) { using namespace std::placeholders; typedef io::raft_control<msgpack::object, msgpack::object> protocol; on<protocol::insert>(std::bind(&control_service_t::insert, this, _1, _2)); on<protocol::erase>(std::bind(&control_service_t::erase, this, _1, _2)); on<protocol::lock>(std::bind(&control_service_t::lock, this, _1)); on<protocol::reset>(std::bind(&control_service_t::reset, this, _1, _2)); on<protocol::dump>(std::bind(&control_service_t::dump, this)); on<protocol::status>(std::bind(&control_service_t::status, this, _1)); on<protocol::leader>(std::bind(&control_service_t::leader, this, _1)); options_t options; // The format of the name is "service/<name of service from the config>". // So here we drop "service/" part to determine the raft control service name. // TODO: Make the context to pass the names to services without "service/" part. options.control_service_name = name.substr(8, std::string::npos); options.node_service_name = "raft_node"; options.configuration_machine_name = "configuration"; options.some_nodes = args.as_object().at("some_nodes", dynamic_t::empty_array).to<std::set<node_id_t>>(); options.election_timeout = args.as_object().at("election_timeout", 500).to<unsigned int>(); options.heartbeat_timeout = args.as_object().at("heartbeat_timeout", options.election_timeout / 2).to<unsigned int>(); options.snapshot_threshold = args.as_object().at("snapshot_threshold", 100000).to<unsigned int>(); options.message_size = args.as_object().at("message_size", 1000).to<unsigned int>(); m_context.raft().set_options(options); m_context.raft().activate(); if(m_context.config.create_raft_cluster) { typedef log_entry<configuration_machine_t> entry_type; typedef configuration<configuration_machine_t> config_type; typedef log_traits<configuration_machine_t, config_type::cluster_type>::snapshot_type snapshot_type; config_type config(m_context, m_context.raft().options().configuration_machine_name, cluster_config_t {std::set<node_id_t>(), boost::none}); configuration_machine_t config_machine(m_context, *m_context.raft().m_reactor, *this); config.log().push(entry_type()); config.log().push(entry_type()); std::map<std::string, lockable_config_t> config_snapshot; config_snapshot[m_context.raft().options().configuration_machine_name] = lockable_config_t { false, cluster_config_t { std::set<node_id_t>({m_context.raft().id()}), boost::none } }; config_machine.consume(config_snapshot); config.log().set_snapshot(1, 1, snapshot_type(std::move(config_snapshot), config.cluster())); config.set_current_term(std::max<uint64_t>(1, config.current_term())); config.set_commit_index(1); config.set_last_applied(1); m_config_actor = m_context.raft().create_cluster( m_context.raft().options().configuration_machine_name, std::move(config_machine), std::move(config) ); } else { m_config_actor = m_context.raft().insert( m_context.raft().options().configuration_machine_name, configuration_machine_t(m_context, *m_context.raft().m_reactor, *this) ); } // Run Raft node service. auto node_service_reactor = std::make_shared<io::reactor_t>(); std::unique_ptr<api::service_t> node_service(std::make_unique<node_service_t>( m_context, *m_context.raft().m_reactor, std::string("service/") + m_context.raft().options().node_service_name )); std::unique_ptr<actor_t> node_service_actor( new actor_t(m_context, m_context.raft().m_reactor, std::move(node_service)) ); m_context.insert(m_context.raft().options().node_service_name, std::move(node_service_actor)); }
uniresis_t::uniresis_t(context_t& context, asio::io_service& loop, const std::string& name, const dynamic_t& args) : api::service_t(context, loop, name, args), dispatch<io::uniresis_tag>(name), uuid(context.uuid()), resources(), updater(nullptr), log(context.log("uniresis")) { if (resources.cpu == 0) { throw std::system_error(uniresis::uniresis_errc::failed_calculate_cpu_count); } if (resources.mem == 0) { throw std::system_error(uniresis::uniresis_errc::failed_calculate_system_memory); } auto restrictions = args.as_object().at("restrictions", dynamic_t::empty_object).as_object(); auto cpu_restricted = std::min( resources.cpu, static_cast<uint>(restrictions.at("cpu", resources.cpu).as_uint()) ); if (resources.cpu != cpu_restricted) { resources.cpu = cpu_restricted; COCAINE_LOG_INFO(log, "restricted available CPU count to {}", resources.cpu); } auto mem_restricted = std::min( resources.mem, static_cast<std::uint64_t>(restrictions.at("mem", resources.mem).as_uint()) ); if (resources.mem != mem_restricted) { resources.mem = mem_restricted; COCAINE_LOG_INFO(log, "restricted available system memory to {}", resources.mem); } auto prefix = args.as_object().at("prefix", defaults::resources_path).as_string(); auto path = format("{}/{}", prefix, uuid); auto hostname = context.config().network().hostname(); auto endpoints = resolve(hostname); dynamic_t::object_t extra; if (auto locator = context.config().services().get("locator")) { extra = dynamic_converter<dynamic_t::object_t>::convert( locator->args().as_object().at("extra_param", dynamic_t::empty_object) ); } auto unicorn = api::unicorn(context, args.as_object().at("unicorn", defaults::unicorn_name).as_string()); updater = std::make_shared<updater_t>( std::move(path), std::move(hostname), std::move(endpoints), std::move(extra), resources, std::move(unicorn), log ); updater->notify(); on<io::uniresis::cpu_count>([&] { return resources.cpu; }); on<io::uniresis::memory_count>([&] { return resources.mem; }); on<io::uniresis::uuid>([&] { return uuid; }); }
config_t::config_t(const std::string& config_path) { path.config = config_path; const auto config_file_status = fs::status(path.config); if(!fs::exists(config_file_status) || !fs::is_regular_file(config_file_status)) { throw cocaine::error_t("the configuration file path is invalid"); } fs::ifstream stream(path.config); if(!stream) { throw cocaine::error_t("unable to read the configuration file"); } rapidjson::MemoryPoolAllocator<> json_allocator; rapidjson::Reader json_reader(&json_allocator); rapidjson_ifstream_t config_stream(&stream); dynamic_reader_t config_constructor; if(!json_reader.Parse<rapidjson::kParseDefaultFlags>(config_stream, config_constructor)) { if(json_reader.HasParseError()) { throw cocaine::error_t("the configuration file is corrupted - %s", json_reader.GetParseError()); } else { throw cocaine::error_t("the configuration file is corrupted"); } } const dynamic_t root(config_constructor.Result()); const auto &path_config = root.as_object().at("paths", dynamic_t::empty_object).as_object(); const auto &locator_config = root.as_object().at("locator", dynamic_t::empty_object).as_object(); const auto &network_config = root.as_object().at("network", dynamic_t::empty_object).as_object(); // Validation if(root.as_object().at("version", 0).to<unsigned int>() != 2) { throw cocaine::error_t("the configuration file version is invalid"); } // Paths path.plugins = path_config.at("plugins", defaults::plugins_path).as_string(); path.runtime = path_config.at("runtime", defaults::runtime_path).as_string(); const auto runtime_path_status = fs::status(path.runtime); if(!fs::exists(runtime_path_status)) { throw cocaine::error_t("the %s directory does not exist", path.runtime); } else if(!fs::is_directory(runtime_path_status)) { throw cocaine::error_t("the %s path is not a directory", path.runtime); } // Hostname configuration char hostname[256]; if(gethostname(hostname, 256) != 0) { throw std::system_error(errno, std::system_category(), "unable to determine the hostname"); } addrinfo hints, *result = nullptr; std::memset(&hints, 0, sizeof(addrinfo)); hints.ai_flags = AI_CANONNAME; const int rv = getaddrinfo(hostname, nullptr, &hints, &result); if(rv != 0) { throw std::system_error(rv, gai_category(), "unable to determine the hostname"); } network.hostname = locator_config.at("hostname", std::string(result->ai_canonname)).as_string(); network.uuid = unique_id_t().string(); freeaddrinfo(result); // Locator configuration network.endpoint = locator_config.at("endpoint", defaults::endpoint).as_string(); network.locator = locator_config.at("port", defaults::locator_port).to<uint16_t>(); // WARNING: Now only arrays of two items are allowed. auto ports = locator_config.find("port-range"); if(ports != locator_config.end()) { network.ports = ports->second.to<std::tuple<uint16_t, uint16_t>>(); } // Cluster configuration if(!network_config.empty()) { if(network_config.count("group") == 1) { network.group = network_config["group"].as_string(); } if(network_config.count("gateway") == 1) { network.gateway = { network_config["gateway"].as_object().at("type", "adhoc").as_string(), network_config["gateway"].as_object().at("args", dynamic_t::empty_object) }; } } #ifdef COCAINE_ALLOW_RAFT create_raft_cluster = false; #endif // Component configuration logging = root.as_object().at("logging" , dynamic_t::empty_object).to<config_t::logging_t>(); services = root.as_object().at("services", dynamic_t::empty_object).to<config_t::component_map_t>(); storages = root.as_object().at("storages", dynamic_t::empty_object).to<config_t::component_map_t>(); }
node_t::node_t(context_t& context, boost::asio::io_service& asio, const std::string& name, const dynamic_t& args): category_type(context, asio, name, args), dispatch<node_tag>(name), m_context(context), m_log(context.log(name)) { using namespace std::placeholders; on<node::start_app>(std::bind(&node_t::on_start_app, this, _1, _2)); on<node::pause_app>(std::bind(&node_t::on_pause_app, this, _1)); on<node::list>(std::bind(&node_t::on_list, this)); const auto runlist_id = args.as_object().at("runlist", "default").as_string(); const auto storage = api::storage(m_context, "core"); typedef std::map<std::string, std::string> runlist_t; runlist_t runlist; { blackhole::scoped_attributes_t attributes(*m_log, { blackhole::attribute::make("runlist", runlist_id) }); COCAINE_LOG_INFO(m_log, "reading runlist"); try { runlist = storage->get<runlist_t>("runlists", runlist_id); } catch(const storage_error_t& e) { COCAINE_LOG_WARNING(m_log, "unable to read runlist: %s", e.what()); } } if(runlist.empty()) { return; } COCAINE_LOG_INFO(m_log, "starting %d app(s)", runlist.size()); std::vector<std::string> errored; for(auto it = runlist.begin(); it != runlist.end(); ++it) { blackhole::scoped_attributes_t attributes(*m_log, { blackhole::attribute::make("app", it->first) }); try { on_start_app(it->first, it->second); } catch(const std::exception& e) { COCAINE_LOG_ERROR(m_log, "unable to initialize app: %s", e.what()); errored.push_back(it->first); } catch(...) { COCAINE_LOG_ERROR(m_log, "unable to initialize app"); errored.push_back(it->first); } } if(!errored.empty()) { std::ostringstream stream; std::ostream_iterator<char> builder(stream); boost::spirit::karma::generate(builder, boost::spirit::karma::stream % ", ", errored); COCAINE_LOG_ERROR(m_log, "couldn't start %d app(s): %s", errored.size(), stream.str()); } }
node_t::node_t(context_t& context, asio::io_service& asio, const std::string& name, const dynamic_t& args): category_type(context, asio, name, args), dispatch<io::node_tag>(name), context(context), log(context.log(name)) { on<io::node::start_app>(std::bind(&node_t::start_app, this, ph::_1, ph::_2)); on<io::node::pause_app>(std::bind(&node_t::pause_app, this, ph::_1)); on<io::node::list> (std::bind(&node_t::list, this)); on<io::node::info> (std::bind(&node_t::info, this, ph::_1, ph::_2)); // Context signal/slot. signal = std::make_shared<dispatch<io::context_tag>>(name); signal->on<io::context::shutdown>(std::bind(&node_t::on_context_shutdown, this)); const auto runname = args.as_object().at("runlist", "").as_string(); if(runname.empty()) { context.listen(signal, asio); return; } COCAINE_LOG_INFO(log, "reading '%s' runlist", runname); typedef std::map<std::string, std::string> runlist_t; runlist_t runlist; const auto storage = api::storage(context, "core"); try { // TODO: Perform request to a special service, like "storage->runlist(runname)". runlist = storage->get<runlist_t>("runlists", runname); } catch(const std::system_error& err) { COCAINE_LOG_WARNING(log, "unable to read '%s' runlist: %s", runname, err.what()); } if(runlist.empty()) { context.listen(signal, asio); return; } COCAINE_LOG_INFO(log, "starting %d app(s)", runlist.size()); std::vector<std::string> errored; for(auto it = runlist.begin(); it != runlist.end(); ++it) { blackhole::scoped_attributes_t scope(*log, {{ "app", it->first }}); try { start_app(it->first, it->second); } catch(const std::exception& e) { COCAINE_LOG_WARNING(log, "unable to initialize app: %s", e.what()); errored.push_back(it->first); } } if(!errored.empty()) { std::ostringstream stream; std::ostream_iterator<char> builder(stream); boost::spirit::karma::generate(builder, boost::spirit::karma::string % ", ", errored); COCAINE_LOG_WARNING(log, "couldn't start %d app(s): %s", errored.size(), stream.str()); } context.listen(signal, asio); }
files_t::files_t(context_t& context, const std::string& name, const dynamic_t& args): category_type(context, name, args), m_log(context.log(name)), m_parent_path(args.as_object().at("path").as_string()) { }