| // Licensed to the Apache Software Foundation (ASF) under one |
| // or more contributor license agreements. See the NOTICE file |
| // distributed with this work for additional information |
| // regarding copyright ownership. The ASF licenses this file |
| // to you under the Apache License, Version 2.0 (the |
| // "License"); you may not use this file except in compliance |
| // with the License. You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| #include <stdint.h> |
| |
| #include <vector> |
| #include <utility> |
| |
| #include <mesos/mesos.hpp> |
| |
| #include <mesos/module/anonymous.hpp> |
| |
| #include <mesos/slave/resource_estimator.hpp> |
| |
| #include <process/owned.hpp> |
| |
| #include <stout/check.hpp> |
| #include <stout/flags.hpp> |
| #include <stout/hashset.hpp> |
| #include <stout/nothing.hpp> |
| #include <stout/os.hpp> |
| #include <stout/stringify.hpp> |
| #include <stout/try.hpp> |
| |
| #include "common/build.hpp" |
| |
| #include "hook/manager.hpp" |
| |
| #ifdef __linux__ |
| #include "linux/systemd.hpp" |
| #endif // __linux__ |
| |
| #include "logging/logging.hpp" |
| |
| #include "master/detector.hpp" |
| |
| #include "messages/flags.hpp" |
| #include "messages/messages.hpp" |
| |
| #include "module/manager.hpp" |
| |
| #include "slave/gc.hpp" |
| #include "slave/slave.hpp" |
| #include "slave/status_update_manager.hpp" |
| |
| #include "version/version.hpp" |
| |
| using namespace mesos::internal; |
| using namespace mesos::internal::slave; |
| |
| using mesos::modules::Anonymous; |
| using mesos::modules::ModuleManager; |
| |
| using mesos::slave::QoSController; |
| using mesos::slave::ResourceEstimator; |
| |
| using mesos::SlaveInfo; |
| |
| using process::Owned; |
| |
| using process::firewall::DisabledEndpointsFirewallRule; |
| using process::firewall::FirewallRule; |
| |
| using std::cerr; |
| using std::cout; |
| using std::endl; |
| using std::move; |
| using std::string; |
| using std::vector; |
| |
| |
| void version() |
| { |
| cout << "mesos" << " " << MESOS_VERSION << endl; |
| } |
| |
| |
| int main(int argc, char** argv) |
| { |
| GOOGLE_PROTOBUF_VERIFY_VERSION; |
| |
| slave::Flags flags; |
| |
| // The following flags are executable specific (e.g., since we only |
| // have one instance of libprocess per execution, we only want to |
| // advertise the IP and port option once, here). |
| Option<string> ip; |
| flags.add(&ip, "ip", "IP address to listen on"); |
| |
| uint16_t port; |
| flags.add(&port, "port", "Port to listen on", SlaveInfo().port()); |
| |
| Option<string> advertise_ip; |
| flags.add(&advertise_ip, |
| "advertise_ip", |
| "IP address advertised to reach mesos slave.\n" |
| "Mesos slave does not bind using this IP address.\n" |
| "However, this IP address may be used to access Mesos slave."); |
| |
| Option<string> advertise_port; |
| flags.add(&advertise_port, |
| "advertise_port", |
| "Port advertised to reach mesos slave (alongwith advertise_ip).\n" |
| "Mesos slave does not bind using this port.\n" |
| "However, this port (alongwith advertise_ip) may be used to\n" |
| "access Mesos slave."); |
| |
| Option<string> master; |
| flags.add(&master, |
| "master", |
| "May be one of:\n" |
| " host:port\n" |
| " zk://host1:port1,host2:port2,.../path\n" |
| " zk://username:password@host1:port1,host2:port2,.../path\n" |
| " file:///path/to/file (where file contains one of the above)"); |
| |
| |
| // Optional IP discover script that will set the slave's IP. |
| // If set, its output is expected to be a valid parseable IP string. |
| Option<string> ip_discovery_command; |
| flags.add(&ip_discovery_command, |
| "ip_discovery_command", |
| "Optional IP discovery binary: if set, it is expected to emit\n" |
| "the IP address which slave will try to bind to.\n" |
| "Cannot be used in conjunction with --ip."); |
| |
| Try<Nothing> load = flags.load("MESOS_", argc, argv); |
| |
| // TODO(marco): this pattern too should be abstracted away |
| // in FlagsBase; I have seen it at least 15 times. |
| if (load.isError()) { |
| cerr << flags.usage(load.error()) << endl; |
| return EXIT_FAILURE; |
| } |
| |
| if (flags.help) { |
| cout << flags.usage() << endl; |
| return EXIT_SUCCESS; |
| } |
| |
| if (flags.version) { |
| version(); |
| return EXIT_SUCCESS; |
| } |
| |
| if (master.isNone()) { |
| cerr << flags.usage("Missing required option --master") << endl; |
| return EXIT_FAILURE; |
| } |
| |
| // Initialize modules. Note that since other subsystems may depend |
| // upon modules, we should initialize modules before anything else. |
| if (flags.modules.isSome()) { |
| Try<Nothing> result = ModuleManager::load(flags.modules.get()); |
| if (result.isError()) { |
| EXIT(EXIT_FAILURE) << "Error loading modules: " << result.error(); |
| } |
| } |
| |
| // Initialize hooks. |
| if (flags.hooks.isSome()) { |
| Try<Nothing> result = HookManager::initialize(flags.hooks.get()); |
| if (result.isError()) { |
| EXIT(EXIT_FAILURE) << "Error installing hooks: " << result.error(); |
| } |
| } |
| |
| // Initialize libprocess. |
| if (ip_discovery_command.isSome() && ip.isSome()) { |
| EXIT(EXIT_FAILURE) << flags.usage( |
| "Only one of --ip or --ip_discovery_command should be specified"); |
| } |
| |
| if (ip_discovery_command.isSome()) { |
| Try<string> ipAddress = os::shell(ip_discovery_command.get()); |
| |
| if (ipAddress.isError()) { |
| EXIT(EXIT_FAILURE) << ipAddress.error(); |
| } |
| |
| os::setenv("LIBPROCESS_IP", strings::trim(ipAddress.get())); |
| } else if (ip.isSome()) { |
| os::setenv("LIBPROCESS_IP", ip.get()); |
| } |
| |
| os::setenv("LIBPROCESS_PORT", stringify(port)); |
| |
| if (advertise_ip.isSome()) { |
| os::setenv("LIBPROCESS_ADVERTISE_IP", advertise_ip.get()); |
| } |
| |
| if (advertise_port.isSome()) { |
| os::setenv("LIBPROCESS_ADVERTISE_PORT", advertise_port.get()); |
| } |
| |
| process::initialize("slave(1)"); |
| |
| logging::initialize(argv[0], flags, true); // Catch signals. |
| |
| spawn(new VersionProcess(), true); |
| |
| LOG(INFO) << "Build: " << build::DATE << " by " << build::USER; |
| |
| LOG(INFO) << "Version: " << MESOS_VERSION; |
| |
| if (build::GIT_TAG.isSome()) { |
| LOG(INFO) << "Git tag: " << build::GIT_TAG.get(); |
| } |
| |
| if (build::GIT_SHA.isSome()) { |
| LOG(INFO) << "Git SHA: " << build::GIT_SHA.get(); |
| } |
| |
| Fetcher fetcher; |
| |
| #ifdef __linux__ |
| // Initialize systemd if it exists. |
| if (systemd::exists() && flags.systemd_enable_support) { |
| LOG(INFO) << "Inializing systemd state"; |
| |
| systemd::Flags systemdFlags; |
| systemdFlags.enabled = flags.systemd_enable_support; |
| systemdFlags.runtime_directory = flags.systemd_runtime_directory; |
| systemdFlags.cgroups_hierarchy = flags.cgroups_hierarchy; |
| |
| Try<Nothing> initialize = systemd::initialize(systemdFlags); |
| if (initialize.isError()) { |
| EXIT(EXIT_FAILURE) |
| << "Failed to initialize systemd: " + initialize.error(); |
| } |
| } |
| #endif // __linux__ |
| |
| Try<Containerizer*> containerizer = |
| Containerizer::create(flags, false, &fetcher); |
| |
| if (containerizer.isError()) { |
| EXIT(EXIT_FAILURE) |
| << "Failed to create a containerizer: " << containerizer.error(); |
| } |
| |
| Try<MasterDetector*> detector = MasterDetector::create(master.get()); |
| |
| if (detector.isError()) { |
| EXIT(EXIT_FAILURE) |
| << "Failed to create a master detector: " << detector.error(); |
| } |
| |
| if (flags.firewall_rules.isSome()) { |
| vector<Owned<FirewallRule>> rules; |
| |
| const Firewall firewall = flags.firewall_rules.get(); |
| |
| if (firewall.has_disabled_endpoints()) { |
| hashset<string> paths; |
| |
| foreach (const string& path, firewall.disabled_endpoints().paths()) { |
| paths.insert(path); |
| } |
| |
| rules.emplace_back(new DisabledEndpointsFirewallRule(paths)); |
| } |
| |
| process::firewall::install(move(rules)); |
| } |
| |
| // Create anonymous modules. |
| foreach (const string& name, ModuleManager::find<Anonymous>()) { |
| Try<Anonymous*> create = ModuleManager::create<Anonymous>(name); |
| if (create.isError()) { |
| EXIT(EXIT_FAILURE) |
| << "Failed to create anonymous module named '" << name << "'"; |
| } |
| |
| // We don't bother keeping around the pointer to this anonymous |
| // module, when we exit that will effectively free it's memory. |
| // |
| // TODO(benh): We might want to add explicit finalization (and |
| // maybe explicit initialization too) in order to let the module |
| // do any housekeeping necessary when the slave is cleanly |
| // terminating. |
| } |
| |
| Files files; |
| GarbageCollector gc; |
| StatusUpdateManager statusUpdateManager(flags); |
| |
| Try<ResourceEstimator*> resourceEstimator = |
| ResourceEstimator::create(flags.resource_estimator); |
| |
| if (resourceEstimator.isError()) { |
| cerr << "Failed to create resource estimator: " |
| << resourceEstimator.error() << endl; |
| return EXIT_FAILURE; |
| } |
| |
| Try<QoSController*> qosController = |
| QoSController::create(flags.qos_controller); |
| |
| if (qosController.isError()) { |
| cerr << "Failed to create QoS Controller: " |
| << qosController.error() << endl; |
| return EXIT_FAILURE; |
| } |
| |
| |
| LOG(INFO) << "Starting Mesos slave"; |
| |
| Slave* slave = new Slave( |
| flags, |
| detector.get(), |
| containerizer.get(), |
| &files, |
| &gc, |
| &statusUpdateManager, |
| resourceEstimator.get(), |
| qosController.get()); |
| |
| process::spawn(slave); |
| process::wait(slave->self()); |
| |
| delete slave; |
| |
| delete resourceEstimator.get(); |
| |
| delete qosController.get(); |
| |
| delete detector.get(); |
| |
| delete containerizer.get(); |
| |
| return EXIT_SUCCESS; |
| } |