blob: b48d894ed59170849f4b4ac5e37842af6c421e81 [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 __DOCKER_HPP__
#define __DOCKER_HPP__
#include <map>
#include <mutex>
#include <string>
#include <utility>
#include <vector>
#include <process/future.hpp>
#include <process/owned.hpp>
#include <process/subprocess.hpp>
#include <stout/duration.hpp>
#include <stout/json.hpp>
#include <stout/none.hpp>
#include <stout/nothing.hpp>
#include <stout/option.hpp>
#include <stout/path.hpp>
#include <stout/version.hpp>
#include <stout/os/rm.hpp>
#include "mesos/resources.hpp"
#include "messages/flags.hpp"
// OS-specific default prefix to be used for the DOCKER_HOST environment
// variable. Note that on Linux, the default prefix is the only prefix
// available; only Windows supports multiple prefixes.
// TODO(hausdorff): Add support for the Windows `tcp://` prefix as well.
#ifdef __WINDOWS__
constexpr char DEFAULT_DOCKER_HOST_PREFIX[] = "npipe://";
#else
constexpr char DEFAULT_DOCKER_HOST_PREFIX[] = "unix://";
#endif // __WINDOWS__
// Abstraction for working with Docker (modeled on CLI).
//
// TODO(benh): Make futures returned by functions be discardable.
class Docker
{
public:
// Create Docker abstraction and optionally validate docker.
static Try<process::Owned<Docker>> create(
const std::string& path,
const std::string& socket,
bool validate = true,
const Option<JSON::Object>& config = None());
virtual ~Docker() {}
struct Device
{
Path hostPath;
Path containerPath;
struct Access
{
Access() : read(false), write(false), mknod(false) {}
bool read;
bool write;
bool mknod;
} access;
};
struct PortMapping
{
uint32_t hostPort;
uint32_t containerPort;
Option<std::string> protocol;
};
class Container
{
public:
static Try<Container> create(
const std::string& output);
// Returns the docker inspect output.
const std::string output;
// Returns the ID of the container.
const std::string id;
// Returns the name of the container.
const std::string name;
// Returns the pid of the container, or None if the container is
// not running.
const Option<pid_t> pid;
// Returns if the container has already started. This field is
// needed since pid is empty when the container terminates.
const bool started;
// Returns the IPv4 address of the container, or `None()` if no
// IPv4 address has been assigned.
const Option<std::string> ipAddress;
// Returns the IPv6 address of the container, or `None()` if no
// IPv6 address has been assigned.
const Option<std::string> ip6Address;
const std::vector<Device> devices;
// Returns the DNS nameservers set by "--dns" option.
const std::vector<std::string> dns;
// Returns the DNS options set by "--dns-option" option.
const std::vector<std::string> dnsOptions;
// Returns the DNS search domains set by "--dns-search" option.
const std::vector<std::string> dnsSearch;
private:
Container(
const std::string& _output,
const std::string& _id,
const std::string& _name,
const Option<pid_t>& _pid,
bool _started,
const Option<std::string>& _ipAddress,
const Option<std::string>& _ip6Address,
const std::vector<Device>& _devices,
const std::vector<std::string>& _dns,
const std::vector<std::string>& _dnsOptions,
const std::vector<std::string>& _dnsSearch)
: output(_output),
id(_id),
name(_name),
pid(_pid),
started(_started),
ipAddress(_ipAddress),
ip6Address(_ip6Address),
devices(_devices),
dns(_dns),
dnsOptions(_dnsOptions),
dnsSearch(_dnsSearch) {}
};
class Image
{
public:
static Try<Image> create(const JSON::Object& json);
Option<std::vector<std::string>> entrypoint;
Option<std::map<std::string, std::string>> environment;
private:
Image(const Option<std::vector<std::string>>& _entrypoint,
const Option<std::map<std::string, std::string>>& _environment)
: entrypoint(_entrypoint),
environment(_environment) {}
};
// See https://docs.docker.com/engine/reference/run for a complete
// explanation of each option.
class RunOptions
{
public:
static Try<RunOptions> create(
const mesos::ContainerInfo& containerInfo,
const mesos::CommandInfo& commandInfo,
const std::string& containerName,
const std::string& sandboxDirectory,
const std::string& mappedDirectory,
const Option<mesos::Resources>& resources = None(),
bool enableCfsQuota = false,
const Option<std::map<std::string, std::string>>& env = None(),
const Option<std::vector<Device>>& devices = None(),
const Option<mesos::internal::ContainerDNSInfo>& defaultContainerDNS = None()); // NOLINT(whitespace/line_length)
// "--privileged" option.
bool privileged;
// "--cpu-shares" option.
Option<uint64_t> cpuShares;
// "--cpu-quota" option.
Option<uint64_t> cpuQuota;
// "--memory" option.
Option<Bytes> memory;
// Environment variable overrides. These overrides will be passed
// to docker container through "--env-file" option.
std::map<std::string, std::string> env;
// "--volume" option.
std::vector<std::string> volumes;
// "--volume-driver" option.
Option<std::string> volumeDriver;
// "--network" option.
Option<std::string> network;
// "--hostname" option.
Option<std::string> hostname;
// "--dns" option.
std::vector<std::string> dns;
// "--dns-search" option.
std::vector<std::string> dnsSearch;
// "--dns-opt" option.
std::vector<std::string> dnsOpt;
// Port mappings for "-p" option.
std::vector<PortMapping> portMappings;
// "--device" option.
std::vector<Device> devices;
// "--entrypoint" option.
Option<std::string> entrypoint;
// "--name" option.
Option<std::string> name;
// Additional docker options passed through containerizer.
std::vector<std::string> additionalOptions;
// "IMAGE[:TAG|@DIGEST]" part of docker run.
std::string image;
// Arguments for docker run.
std::vector<std::string> arguments;
};
// Performs 'docker run IMAGE'. Returns the exit status of the
// container. Note that currently the exit status may correspond
// to the exit code from a failure of the docker client or daemon
// rather than the container. Docker >= 1.10 [1] uses the following
// exit statuses inherited from 'chroot':
// 125 if the error is with Docker daemon itself.
// 126 if the contained command cannot be invoked.
// 127 if the contained command cannot be found.
// Exit code of contained command otherwise.
//
// [1]: https://github.com/docker/docker/pull/14012
virtual process::Future<Option<int>> run(
const RunOptions& options,
const process::Subprocess::IO& _stdout =
process::Subprocess::FD(STDOUT_FILENO),
const process::Subprocess::IO& _stderr =
process::Subprocess::FD(STDERR_FILENO)) const;
// Returns the current docker version.
virtual process::Future<Version> version() const;
// Performs 'docker stop -t TIMEOUT CONTAINER'. If remove is true then a rm -f
// will be called when stop failed, otherwise a failure is returned. The
// timeout parameter will be passed through to docker and is the amount of
// time for docker to wait after stopping a container before killing it.
// A value of zero (the default value) is the same as issuing a
// 'docker kill CONTAINER'.
virtual process::Future<Nothing> stop(
const std::string& containerName,
const Duration& timeout = Seconds(0),
bool remove = false) const;
// Performs 'docker kill --signal=<signal> CONTAINER'.
virtual process::Future<Nothing> kill(
const std::string& containerName,
int signal) const;
// Performs 'docker rm (-f) CONTAINER'.
virtual process::Future<Nothing> rm(
const std::string& containerName,
bool force = false) const;
// Performs 'docker inspect CONTAINER'. If retryInterval is set,
// we will keep retrying inspect until the container is started or
// the future is discarded.
virtual process::Future<Container> inspect(
const std::string& containerName,
const Option<Duration>& retryInterval = None()) const;
// Performs 'docker ps (-a)'.
virtual process::Future<std::vector<Container>> ps(
bool all = false,
const Option<std::string>& prefix = None()) const;
virtual process::Future<Image> pull(
const std::string& directory,
const std::string& image,
bool force = false) const;
// Validate current docker version is not less than minVersion.
virtual Try<Nothing> validateVersion(const Version& minVersion) const;
virtual std::string getPath()
{
return path;
}
virtual std::string getSocket()
{
return socket;
}
protected:
// Uses the specified path to the Docker CLI tool.
Docker(const std::string& _path,
const std::string& _socket,
const Option<JSON::Object>& _config)
: path(_path),
socket(DEFAULT_DOCKER_HOST_PREFIX + _socket),
config(_config) {}
private:
static process::Future<Version> _version(
const std::string& cmd,
const process::Subprocess& s);
static process::Future<Version> __version(
const process::Future<std::string>& output);
static process::Future<Nothing> _stop(
const Docker& docker,
const std::string& containerName,
const std::string& cmd,
const process::Subprocess& s,
bool remove);
static void _inspect(
const std::vector<std::string>& argv,
const process::Owned<process::Promise<Container>>& promise,
const Option<Duration>& retryInterval,
std::shared_ptr<std::pair<lambda::function<void()>, std::mutex>>
callback);
static void __inspect(
const std::vector<std::string>& argv,
const process::Owned<process::Promise<Container>>& promise,
const Option<Duration>& retryInterval,
process::Future<std::string> output,
const process::Subprocess& s,
std::shared_ptr<std::pair<lambda::function<void()>, std::mutex>>
callback);
static void ___inspect(
const std::vector<std::string>& argv,
const process::Owned<process::Promise<Container>>& promise,
const Option<Duration>& retryInterval,
const process::Future<std::string>& output,
std::shared_ptr<std::pair<lambda::function<void()>, std::mutex>>
callback);
static process::Future<std::vector<Container>> _ps(
const Docker& docker,
const std::string& cmd,
const process::Subprocess& s,
const Option<std::string>& prefix,
process::Future<std::string> output);
static process::Future<std::vector<Container>> __ps(
const Docker& docker,
const Option<std::string>& prefix,
const std::string& output);
static void inspectBatches(
process::Owned<std::vector<Docker::Container>> containers,
process::Owned<std::vector<std::string>> lines,
process::Owned<process::Promise<std::vector<Docker::Container>>> promise,
const Docker& docker,
const Option<std::string>& prefix);
static std::vector<process::Future<Docker::Container>> createInspectBatch(
process::Owned<std::vector<std::string>> lines,
const Docker& docker,
const Option<std::string>& prefix);
static process::Future<Image> _pull(
const Docker& docker,
const process::Subprocess& s,
const std::string& directory,
const std::string& image,
const std::string& path,
const std::string& socket,
const Option<JSON::Object>& config,
process::Future<std::string> output);
static process::Future<Image> __pull(
const Docker& docker,
const std::string& directory,
const std::string& image,
const std::string& path,
const std::string& socket,
const Option<JSON::Object>& config);
static process::Future<Image> ___pull(
const Docker& docker,
const process::Subprocess& s,
const std::string& cmd,
const std::string& directory,
const std::string& image);
static process::Future<Image> ____pull(
const std::string& output);
static void pullDiscarded(
const process::Subprocess& s,
const std::string& cmd);
const std::string path;
const std::string socket;
const Option<JSON::Object> config;
};
#endif // __DOCKER_HPP__