blob: fffc5705424990b7561b824be025301941946a04 [file] [log] [blame]
// 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
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// See the License for the specific language governing permissions and
// limitations under the License.
#include <map>
#include <mutex>
#include <string>
#include <mesos/mesos.hpp>
// Mesos executor interface and executor driver. An executor is
// responsible for launching tasks in a framework specific way (i.e.,
// creating new threads, new processes, etc). One or more executors
// from the same framework may run concurrently on the same machine.
// Note that we use the term "executor" fairly loosely to refer to the
// code that implements the Executor interface (see below) as well as
// the program that is responsible for instantiating a new
// MesosExecutorDriver (also below). In fact, while a Mesos slave is
// responsible for (forking and) executing the "executor", there is no
// reason why whatever the slave executed might itself actually
// execute another program which actually instantiates and runs the
// MesosSchedulerDriver. The only contract with the slave is that the
// program that it invokes does not exit until the "executor" has
// completed. Thus, what the slave executes may be nothing more than a
// script which actually executes (or forks and waits) the "real"
// executor.
// src/java/src/org/apache/mesos, Python: src/python/src, etc.).
// Forward declaration.
namespace process {
class Latch;
} // namespace process {
namespace mesos {
// A few forward declarations.
class ExecutorDriver;
namespace internal {
class ExecutorProcess;
// Callback interface to be implemented by frameworks' executors. Note
// that only one callback will be invoked at a time, so it is not
// recommended that you block within a callback because it may cause a
// deadlock.
// Each callback includes a pointer to the executor driver that was
// used to run this executor. The pointer will not change for the
// duration of an executor (i.e., from the point you do
// ExecutorDriver::start() to the point that ExecutorDriver::join()
// returns). This is intended for convenience so that an executor
// doesn't need to store a pointer to the driver itself.
// TODO(bmahler): Consider adding a usage() callback here, that
// provides information to the executor about its ResourceUsage.
class Executor
// Empty virtual destructor (necessary to instantiate subclasses).
virtual ~Executor() {}
// Invoked once the executor driver has been able to successfully
// connect with Mesos. In particular, a scheduler can pass some
// data to its executors through the FrameworkInfo.ExecutorInfo's
// data field.
virtual void registered(
ExecutorDriver* driver,
const ExecutorInfo& executorInfo,
const FrameworkInfo& frameworkInfo,
const SlaveInfo& slaveInfo) = 0;
// Invoked when the executor reregisters with a restarted slave.
virtual void reregistered(
ExecutorDriver* driver,
const SlaveInfo& slaveInfo) = 0;
// Invoked when the executor becomes "disconnected" from the slave
// (e.g., the slave is being restarted due to an upgrade).
virtual void disconnected(ExecutorDriver* driver) = 0;
// Invoked when a task has been launched on this executor (initiated
// via Scheduler::launchTasks). Note that this task can be realized
// with a thread, a process, or some simple computation, however, no
// other callbacks will be invoked on this executor until this
// callback has returned.
virtual void launchTask(
ExecutorDriver* driver,
const TaskInfo& task) = 0;
// Invoked when a task running within this executor has been killed
// (via SchedulerDriver::killTask). Note that no status update will
// be sent on behalf of the executor, the executor is responsible
// for creating a new TaskStatus (i.e., with TASK_KILLED) and
// invoking ExecutorDriver::sendStatusUpdate.
virtual void killTask(
ExecutorDriver* driver,
const TaskID& taskId) = 0;
// Invoked when a framework message has arrived for this executor.
// These messages are best effort; do not expect a framework message
// to be retransmitted in any reliable fashion.
virtual void frameworkMessage(
ExecutorDriver* driver,
const std::string& data) = 0;
// Invoked when the executor should terminate all of its currently
// running tasks. Note that after a Mesos has determined that an
// executor has terminated any tasks that the executor did not send
// terminal status updates for (e.g., TASK_KILLED, TASK_FINISHED,
// TASK_FAILED, etc) a TASK_LOST status update will be created.
virtual void shutdown(ExecutorDriver* driver) = 0;
// Invoked when a fatal error has occurred with the executor and/or
// executor driver. The driver will be aborted BEFORE invoking this
// callback.
virtual void error(
ExecutorDriver* driver,
const std::string& message) = 0;
// Abstract interface for connecting an executor to Mesos. This
// interface is used both to manage the executor's lifecycle (start
// it, stop it, or wait for it to finish) and to interact with Mesos
// (e.g., send status updates, send framework messages, etc.). See
// MesosExecutorDriver below for a concrete example of an
// ExecutorDriver.
class ExecutorDriver
// Empty virtual destructor (necessary to instantiate subclasses).
virtual ~ExecutorDriver() {}
// Starts the executor driver. This needs to be called before any
// other driver calls are made.
virtual Status start() = 0;
// Stops the executor driver.
virtual Status stop() = 0;
// Aborts the driver so that no more callbacks can be made to the
// executor. The semantics of abort and stop have deliberately been
// separated so that code can detect an aborted driver (i.e., via
// the return status of ExecutorDriver::join, see below), and
// instantiate and start another driver if desired (from within the
// same process ... although this functionality is currently not
// supported for executors).
virtual Status abort() = 0;
// Waits for the driver to be stopped or aborted, possibly
// _blocking_ the current thread indefinitely. The return status of
// this function can be used to determine if the driver was aborted
// (see mesos.proto for a description of Status).
virtual Status join() = 0;
// Starts and immediately joins (i.e., blocks on) the driver.
virtual Status run() = 0;
// Sends a status update to the framework scheduler, retrying as
// necessary until an acknowledgement has been received or the
// executor is terminated (in which case, a TASK_LOST status update
// will be sent). See Scheduler::statusUpdate for more information
// about status update acknowledgements.
virtual Status sendStatusUpdate(const TaskStatus& status) = 0;
// Sends a message to the framework scheduler. These messages are
// best effort; do not expect a framework message to be
// retransmitted in any reliable fashion.
virtual Status sendFrameworkMessage(const std::string& data) = 0;
// Concrete implementation of an ExecutorDriver that connects an
// Executor with a Mesos slave. The MesosExecutorDriver is
// thread-safe.
// The driver is responsible for invoking the Executor callbacks as it
// communicates with the Mesos slave.
// Note that blocking on the MesosExecutorDriver (e.g., via
// MesosExecutorDriver::join) doesn't affect the executor callbacks in
// anyway because they are handled by a different thread.
// Note that the driver uses GLOG to do its own logging. GLOG flags
// can be set via environment variables, prefixing the flag name with
// "GLOG_", e.g., "GLOG_v=1". For Mesos specific logging flags see
// src/logging/flags.hpp. Mesos flags can also be set via environment
// variables, prefixing the flag name with "MESOS_", e.g.,
// See src/examples/test_executor.cpp for an example of using the
// MesosExecutorDriver.
class MesosExecutorDriver : public ExecutorDriver
// Creates a new driver that uses the specified Executor. Note, the
// executor pointer must outlive the driver.
// Note that the other constructor overload that accepts `environment`
// argument is preferable to this one in a multithreaded environment,
// because the implementation of this one accesses global environment
// which is unsafe due to a potential concurrent modification of the
// environment by another thread.
explicit MesosExecutorDriver(Executor* executor);
// Creates a new driver that uses the specified `Executor` and environment
// variables. Note, the executor pointer must outlive the driver.
explicit MesosExecutorDriver(
Executor* executor,
const std::map<std::string, std::string>& environment);
// This destructor will block indefinitely if
// MesosExecutorDriver::start was invoked successfully (possibly via
// MesosExecutorDriver::run) and MesosExecutorDriver::stop has not
// been invoked.
~MesosExecutorDriver() override;
// See ExecutorDriver for descriptions of these.
Status start() override;
Status stop() override;
Status abort() override;
Status join() override;
Status run() override;
Status sendStatusUpdate(const TaskStatus& status) override;
Status sendFrameworkMessage(const std::string& data) override;
friend class internal::ExecutorProcess;
Executor* executor;
// Libprocess process for communicating with slave.
internal::ExecutorProcess* process;
// Mutex for enforcing serial execution of all non-callbacks.
std::recursive_mutex mutex;
// Latch for waiting until driver terminates.
process::Latch* latch;
// Current status of the driver.
Status status;
std::map<std::string, std::string> environment;
} // namespace mesos {
#endif // __MESOS_EXECUTOR_HPP__