blob: feeb0278a3d8fec8c7177a9a3dc443ee0c198c5c [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
//
// 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.
#ifndef __EXTERNAL_CONTAINERIZER_HPP__
#define __EXTERNAL_CONTAINERIZER_HPP__
#include <list>
#include <string>
#include <tuple>
#include <process/owned.hpp>
#include <process/subprocess.hpp>
#include <stout/hashmap.hpp>
#include <stout/protobuf.hpp>
#include <stout/try.hpp>
#include "slave/state.hpp"
#include "slave/containerizer/containerizer.hpp"
#include "slave/containerizer/mesos/launcher.hpp"
namespace mesos {
namespace internal {
namespace slave {
// The scheme an external containerizer programs have to adhere to is;
//
// COMMAND < INPUT-PROTO > RESULT-PROTO
//
// launch < containerizer::Launch
// update < containerizer::Update
// usage < containerizer::Usage > mesos::ResourceStatistics
// wait < containerizer::Wait > containerizer::Termination
// destroy < containerizer::Destroy
// containers > containerizer::Containers
// recover
//
// 'wait' on the external containerizer side is expected to block
// until the task command/executor has terminated.
//
// Additionally, we have the following environment variable setup
// for external containerizer programs:
// MESOS_LIBEXEC_DIRECTORY = path to mesos-executor, mesos-usage, ...
// MESOS_WORK_DIRECTORY = slave work directory. This should be used
// for distiguishing slave instances.
// MESOS_DEFAULT_CONTAINER_IMAGE = default image as provided via
// slave flags (default_container_image). This variable is provided
// only in calls to 'launch'.
// Check src/examples/python/test_containerizer.py for a rough
// implementation template of this protocol.
// For debugging purposes of an external containerizer program, it
// might be helpful to enable verbose logging on the slave (GLOG_v=2).
class ExternalContainerizerProcess;
class ExternalContainerizer : public Containerizer
{
public:
static Try<ExternalContainerizer*> create(const Flags& flags);
ExternalContainerizer(const Flags& flags);
virtual ~ExternalContainerizer();
virtual process::Future<Nothing> recover(
const Option<state::SlaveState>& state);
virtual process::Future<bool> launch(
const ContainerID& containerId,
const ExecutorInfo& executorInfo,
const std::string& directory,
const Option<std::string>& user,
const SlaveID& slaveId,
const process::PID<Slave>& slavePid,
bool checkpoint);
virtual process::Future<bool> launch(
const ContainerID& containerId,
const TaskInfo& task,
const ExecutorInfo& executorInfo,
const std::string& directory,
const Option<std::string>& user,
const SlaveID& slaveId,
const process::PID<Slave>& slavePid,
bool checkpoint);
virtual process::Future<Nothing> update(
const ContainerID& containerId,
const Resources& resources);
virtual process::Future<ResourceStatistics> usage(
const ContainerID& containerId);
virtual process::Future<containerizer::Termination> wait(
const ContainerID& containerId);
virtual void destroy(const ContainerID& containerId);
virtual process::Future<hashset<ContainerID>> containers();
private:
process::Owned<ExternalContainerizerProcess> process;
};
class ExternalContainerizerProcess
: public process::Process<ExternalContainerizerProcess>
{
public:
ExternalContainerizerProcess(const Flags& flags);
// Recover containerized executors as specified by state. See
// containerizer.hpp:recover for more.
process::Future<Nothing> recover(const Option<state::SlaveState>& state);
// Start the containerized executor.
process::Future<bool> launch(
const ContainerID& containerId,
const Option<TaskInfo>& taskInfo,
const ExecutorInfo& executorInfo,
const std::string& directory,
const Option<std::string>& user,
const SlaveID& slaveId,
const process::PID<Slave>& slavePid,
bool checkpoint);
// Update the container's resources.
process::Future<Nothing> update(
const ContainerID& containerId,
const Resources& resources);
// Gather resource usage statistics for the containerized executor.
process::Future<ResourceStatistics> usage(const ContainerID& containerId);
// Get a future on the containerized executor's Termination.
process::Future<containerizer::Termination> wait(
const ContainerID& containerId);
// Terminate the containerized executor.
void destroy(const ContainerID& containerId);
// Get all active container-id's.
process::Future<hashset<ContainerID>> containers();
private:
// Startup flags.
const Flags flags;
// Information describing a container environment. A sandbox has to
// be prepared before the external containerizer can be invoked.
struct Sandbox
{
Sandbox(const std::string& directory, const Option<std::string>& user)
: directory(directory), user(user) {}
const std::string directory;
const Option<std::string> user;
};
// Information describing a running container.
struct Container
{
Container(const Option<Sandbox>& sandbox)
: sandbox(sandbox), pid(None()), destroying(false) {}
// Keep sandbox information available for subsequent containerizer
// invocations.
Option<Sandbox> sandbox;
// External containerizer pid as per wait-invocation.
// Wait should block on the external containerizer side, hence we
// need to keep its pid for terminating if needed.
Option<pid_t> pid;
process::Promise<containerizer::Termination> termination;
// Is set when container is being destroyed.
bool destroying;
// As described in MESOS-1251, we need to make sure that events
// that are triggered before launch has completed, are in fact
// queued until then to reduce complexity within external
// containerizer program implementations. To achieve that, we
// simply queue all events onto this promise.
// TODO(tillt): Consider adding a timeout when queuing onto this
// promise to account for external containerizer launch
// invocations that got stuck.
process::Promise<Nothing> launched;
Resources resources;
};
// Stores all active containers.
hashmap<ContainerID, process::Owned<Container>> actives;
process::Future<Nothing> _recover(
const Option<state::SlaveState>& state,
const process::Future<Option<int>>& future);
process::Future<Nothing> __recover(
const Option<state::SlaveState>& state,
const hashset<ContainerID>& containers);
process::Future<Nothing> ___recover();
process::Future<bool> _launch(
const ContainerID& containerId,
const process::Future<Option<int>>& future);
void __launch(
const ContainerID& containerId,
const process::Future<bool>& future);
process::Future<containerizer::Termination> _wait(
const ContainerID& containerId);
void __wait(
const ContainerID& containerId,
const process::Future<std::tuple<
process::Future<Result<containerizer::Termination>>,
process::Future<Option<int>>>>& future);
process::Future<Nothing> _update(
const ContainerID& containerId,
const Resources& resources);
process::Future<Nothing> __update(
const ContainerID& containerId,
const process::Future<Option<int>>& future);
process::Future<ResourceStatistics> _usage(
const ContainerID& containerId);
process::Future<ResourceStatistics> __usage(
const ContainerID& containerId,
const process::Future<std::tuple<
process::Future<Result<ResourceStatistics>>,
process::Future<Option<int>>>>& future);
void _destroy(const ContainerID& containerId);
void __destroy(
const ContainerID& containerId,
const process::Future<Option<int>>& future);
process::Future<hashset<ContainerID>> _containers(
const process::Future<std::tuple<
process::Future<Result<containerizer::Containers>>,
process::Future<Option<int>>>>& future);
// Abort a possibly pending "wait" in the external containerizer
// process.
void unwait(const ContainerID& containerId);
// Call back for when the containerizer has terminated all processes
// in the container.
void cleanup(const ContainerID& containerId);
// Invoke the external containerizer with the given command.
Try<process::Subprocess> invoke(
const std::string& command,
const Option<Sandbox>& sandbox = None(),
const Option<std::map<std::string, std::string>>& environment = None());
// Invoke the external containerizer with the given command and
// a protobuf message to be piped into its stdin.
// There can not be an Option<google::protobuf::Message> due to the
// pure virtual members of that class, hence this override is
// needed.
Try<process::Subprocess> invoke(
const std::string& command,
const google::protobuf::Message& message,
const Option<Sandbox>& sandbox = None(),
const Option<std::map<std::string, std::string>>& environment = None());
};
} // namespace slave {
} // namespace internal {
} // namespace mesos {
#endif // __EXTERNAL_CONTAINERIZER_HPP__