mirror of
https://github.com/galaxyproject/galaxy.git
synced 2026-10-24 12:13:33 +08:00
Containers:
- Improve documentation of docker API param mapping - Improve sample containers config file - Fix constraints - A couple other minor bug/logging fixes
This commit is contained in:
+211
-172
@@ -1,232 +1,271 @@
|
||||
---
|
||||
# Galaxy container interface configuration file
|
||||
#
|
||||
# To configure the location of this file, use the `containers_config_file`
|
||||
# setting in galaxy.ini
|
||||
# To configure the location of this file, use the `containers_config_file` setting in galaxy.yml. Additionally, the
|
||||
# containers interface is only used if `enable_beta_containers_interface` is set in galaxy.yml.
|
||||
|
||||
###
|
||||
### Container Interfaces
|
||||
###
|
||||
|
||||
## Define container interfaces beneath the top-level `containers` dictionary. By # default, a single `_default_`
|
||||
## container of type `docker` is defined:
|
||||
# Define container interfaces beneath the top-level `containers` dictionary. By default, a single `_default_` interface
|
||||
# of type `docker` is defined, equivalent to:
|
||||
|
||||
#containers:
|
||||
# _default_:
|
||||
# type: docker
|
||||
# _default_:
|
||||
# type: docker
|
||||
|
||||
## Additional options can be specified are specific to the container type:
|
||||
# The interface name is arbitrary and allows multiple distinct configurations to be defined. The name can be any string,
|
||||
# but `_default_` is used if a component that uses the containers interface does not specify an interface. Currently
|
||||
# only Galaxy Interactive Environments use the containers interface, and do not have a way to specify which interface to
|
||||
# use, so configure under the `_default_` key for now.
|
||||
|
||||
# Additional options can be specified are specific to the container type:
|
||||
|
||||
#containers:
|
||||
# _default_:
|
||||
# type: docker
|
||||
# host: docker.example.org:2376
|
||||
# force_tlsverify: yes
|
||||
# _default_:
|
||||
# type: docker
|
||||
# host: docker.example.org:2376
|
||||
# force_tlsverify: yes
|
||||
|
||||
## Keys in `containers` are used to map container consumers to specific # container consumers. If a mapping is not
|
||||
## configured, the _default_ # configuration will be used. An example of multiple container configurations would be:
|
||||
# Keys in `containers` are used to map container consumers to specific # container consumers. If a mapping is not
|
||||
# configured, the _default_ configuration will be used. An example of multiple container configurations would be:
|
||||
|
||||
#containers:
|
||||
# _default_:
|
||||
# type: docker
|
||||
# ... additional options ...
|
||||
# example_swarm:
|
||||
# type: docker_swarm
|
||||
# ... additional options ...
|
||||
# _default_:
|
||||
# type: docker
|
||||
# ... additional options ...
|
||||
# example_swarm:
|
||||
# type: docker_swarm
|
||||
# ... additional options ...
|
||||
#
|
||||
|
||||
###
|
||||
### Container Types and Supported Options
|
||||
###
|
||||
|
||||
## Command-line equivalent arguments are in [brackets] (if applicable)
|
||||
# Command-line equivalent arguments are in [brackets] (if applicable)
|
||||
|
||||
### All types
|
||||
##
|
||||
## The following options are applicable to all container types:
|
||||
##
|
||||
containers:
|
||||
|
||||
#containers:
|
||||
# _default_:
|
||||
# type: ...
|
||||
#
|
||||
# Supported options for all container types
|
||||
#
|
||||
|
||||
## [`--name` (partial)] Prepend this string to the name of containers created
|
||||
# name_prefix: galaxy_
|
||||
_default_:
|
||||
|
||||
## Prefix to prepend to
|
||||
# [`--name` (partial)] Prepend this string to the name of containers created
|
||||
#name_prefix: galaxy_
|
||||
|
||||
### `docker` and `docker_cli` types
|
||||
##
|
||||
## `docker_cli` interacts with Docker via its command-line interface. In the future `docker` will use the API (currently
|
||||
## it's an alias for `docker_cli`)
|
||||
##
|
||||
#
|
||||
# Supported options for `docker` container type
|
||||
#
|
||||
|
||||
#containers:
|
||||
# _default_:
|
||||
# type: docker
|
||||
local_docker:
|
||||
|
||||
## [`-H`/`--host] Daemon socket(s) to connect to
|
||||
# host: null
|
||||
type: docker
|
||||
|
||||
## [`--tlsverify`] Use TLS and verify the remote
|
||||
# force_tlsverify: no
|
||||
# [`-H`/`--host] Daemon socket(s) to connect to
|
||||
#host: null
|
||||
|
||||
## [`--cpus`] Number of CPUs (default 0.000)
|
||||
# cpus: null
|
||||
# [`--tlsverify`] Use TLS and verify the remote
|
||||
#force_tlsverify: no
|
||||
|
||||
## [`-m`/`--memory`] Memory limit
|
||||
# memory: null
|
||||
# [`--cpus`] Number of CPUs (default 0.000)
|
||||
#cpus: null
|
||||
|
||||
## Default image to run if one is not provided to the run method
|
||||
# image: null
|
||||
# [`-m`/`--memory`] Memory limit
|
||||
#memory: null
|
||||
|
||||
### `docker_swarm` and `docker_swarm_cli` types
|
||||
##
|
||||
## All of the `docker` type arguments are supported. Additionally:
|
||||
##
|
||||
# Default image to run if one is not provided to the run method
|
||||
#image: null
|
||||
|
||||
#containers:
|
||||
# _default_:
|
||||
# type: docker_swarm
|
||||
#
|
||||
# Supported options for `docker_swarm` container type
|
||||
#
|
||||
|
||||
## [`--resverve-cpu` and `--limit-cpu`] Reserve the given number of CPUs when containers are run to prevent other
|
||||
## containers from being scheduled on the same node once all of its CPUs are allocated. Additionally, prevent container
|
||||
## from using more than the given number of CPUs
|
||||
# cpus: null
|
||||
swarm:
|
||||
|
||||
## [`--resverve-memory` and `--limit-memory`] Reserve the given amount of memory when containers are run to prevent
|
||||
## other containers from being scheduled on the same node once all of its memory is allocated. Additionally, prevent
|
||||
## container from using more than the given amount of memory
|
||||
# memory: null
|
||||
type: docker_swarm
|
||||
|
||||
## If set, only nodes whose names begin with this string will be visible to and managed by the swarm manager. Note that
|
||||
## regardless of whether this is set, the swarm manager will not attempt to control manager nodes
|
||||
# node_prefix: null
|
||||
# All of the `docker` interface type arguments are supported. Additionally:
|
||||
|
||||
# Convert image from name[:tag] form to name@digest form when possible, to
|
||||
# avoid dependency on the image registry (e.g. Docker Hub or wherever the image
|
||||
# was pulled from) at service creation time. For details see:
|
||||
# https://github.com/docker/docker/issues/31427
|
||||
# resolve_image_digest: no
|
||||
# [`--resverve-cpu` and `--limit-cpu`] Reserve the given number of CPUs when containers are run to prevent other
|
||||
# containers from being scheduled on the same node once all of its CPUs are allocated. Additionally, prevent
|
||||
# container from using more than the given number of CPUs.
|
||||
#cpus: null
|
||||
|
||||
# If a container run request is received that defines volumes to attach, should those volumes be ignored? Swarm mode
|
||||
# does not support mounting volumes.
|
||||
# ignore_volumes: no
|
||||
# [`--resverve-memory` and `--limit-memory`] Reserve the given amount of memory when containers are run to
|
||||
# prevent other containers from being scheduled on the same node once all of its memory is allocated.
|
||||
# Additionally, prevent container from using more than the given amount of memory.
|
||||
#memory: null
|
||||
|
||||
## For the following `service_create_*_constraint` options, if set, the swarm manager automatically sets a corresponding
|
||||
## label on nodes it spawns for services with the given constraints.
|
||||
# If set, only nodes whose names begin with this string will be visible to and managed by the swarm manager.
|
||||
# Note that regardless of whether this is set, the swarm manager will not attempt to control manager nodes
|
||||
#node_prefix: null
|
||||
|
||||
## [`--constraint node.labels._galaxy_image=={image}`]
|
||||
## Automatically create a constraint based on the requested image name when new services are created (the image name is
|
||||
## the resolved form if `resolve_image_digest` is set). This is useful if your nodes do not all have all of the possible
|
||||
## images you'll want to run.
|
||||
# service_create_image_constraint: no
|
||||
# Convert image from name[:tag] form to name@digest form when possible, to avoid dependency on the image
|
||||
# registry (e.g. Docker Hub or wherever the image was pulled from) at service creation time. For details see:
|
||||
# https://github.com/docker/docker/issues/31427
|
||||
#resolve_image_digest: no
|
||||
|
||||
## [`--constraint node.labels._galaxy_cpus=={cpus}`]
|
||||
## Automatically create a constraint based on the requested number of CPUs (or 1, if `cpus` is unset). This is useful if
|
||||
## you are spawning nodes with a mixture of CPU counts and want to prevent (for example) 1-cpu services from being
|
||||
## scheduled on 2-cpu nodes when 2-cpu jobs are waiting.
|
||||
# service_create_cpus_constraint: no
|
||||
# If a container run request is received that defines volumes to attach, should those volumes be ignored? Swarm
|
||||
# mode does not support mounting volumes.
|
||||
#ignore_volumes: no
|
||||
|
||||
## Use the swarm manager to manage services on this swarm. Even if your swarm is static, using the manager is
|
||||
## recommended as the manager will automatically remove services after they have terminated.
|
||||
# managed: yes
|
||||
#
|
||||
# For the following `service_create_*_constraint` options, if set, the swarm manager automatically sets a
|
||||
# corresponding label on nodes it spawns for services with the given constraints.
|
||||
#
|
||||
|
||||
## Automatically start the swarm manager when new services are created. Useful if you want to run the swarm manager but
|
||||
## control it separately from Galaxy.
|
||||
# manager_autostart: yes
|
||||
# [`--constraint node.labels._galaxy_image=={image}`] Automatically create a constraint based on the requested
|
||||
# image name when new services are created (the image name is the resolved form if `resolve_image_digest` is
|
||||
# set). This is useful if your nodes do not all have all of the possible images you'll want to run. Note: It's
|
||||
# currently only possible for nodes to have a single image constraint defined. If your nodes have multiple
|
||||
# images available, do not use this option.
|
||||
#service_create_image_constraint: no
|
||||
|
||||
### `docker_swarm` swarm manager options
|
||||
# [`--constraint node.labels._galaxy_cpus=={cpus}`] Automatically create a constraint based on the requested
|
||||
# number of CPUs (or 1, if `cpus` is unset). This is useful if you are spawning nodes with a mixture of CPU
|
||||
# counts and want to prevent (for example) 1-cpu services from being scheduled on 2-cpu nodes when 2-cpu jobs
|
||||
# are waiting.
|
||||
#service_create_cpus_constraint: no
|
||||
|
||||
## Galaxy can manage a Docker swarm using a built-in daemon and callouts to commands to add/remove nodes from your swarm.
|
||||
## These options are set on `docker_swarm` type containers underneath a `manager_conf` key, like so:
|
||||
#
|
||||
# Galaxy can manage a Docker swarm using a built-in daemon and callouts to commands to add/remove nodes from
|
||||
# your swarm.
|
||||
#
|
||||
|
||||
#containers:
|
||||
# _default_:
|
||||
# type: docker_swarm
|
||||
# manager_conf:
|
||||
# spawn_command: openstack server create ...
|
||||
# ...
|
||||
# Use the swarm manager to manage services on this swarm. Even if your swarm is static, using the manager is
|
||||
# recommended as the manager will automatically remove services after they have terminated.
|
||||
#managed: yes
|
||||
|
||||
## When the swarm manager daemonizes, it writes a pid file so that only one manager will run at a time. This is the path
|
||||
## to that pid file. {xdg_data_home} will be templated automatically and defaults to ~/.local/share as per the XDG
|
||||
## specification
|
||||
# pid_file: '{xdg_data_home}/galaxy_swarm_manager.pid'
|
||||
# Automatically start the swarm manager when new services are created. Useful if you want to run the swarm
|
||||
# manager but control it separately from Galaxy.
|
||||
#manager_autostart: yes
|
||||
|
||||
## Program output will be written to the log
|
||||
# log_file: '{xdg_data_home}/galaxy_swarm_manager.log'
|
||||
# Configuration dictionary for the swarm manager
|
||||
manager_conf:
|
||||
|
||||
## Level of log messages (levels should be Python logging module level names (case insensitive)
|
||||
# log_level: INFO
|
||||
#
|
||||
# In order to use the swarm manager for more than just service cleanup, (e.g. its node spawn and destroy
|
||||
# features), you'll need to configure the `cpus` option in the interface. Otherwise, there are no limits on
|
||||
# the number of services that will be assigned to a given node, so the swarm manager has no way to determine
|
||||
# that more nodes should be spawned.
|
||||
#
|
||||
# Each service consumes one "slot", and the number of slots available on a node is its number of CPUs
|
||||
# divided by the value of `cpus`.
|
||||
#
|
||||
|
||||
## Log message format
|
||||
# log_format: %(name)s %(levelname)s %(asctime)s %(message)s
|
||||
# When the swarm manager daemonizes, it writes a pid file so that only one manager will run at a time. This
|
||||
# is the path to that pid file. {xdg_data_home} will be templated automatically and defaults to
|
||||
# ~/.local/share as per the XDG specification
|
||||
#pid_file: '{xdg_data_home}/galaxy_swarm_manager.pid'
|
||||
|
||||
## Limits:
|
||||
##
|
||||
## - service_wait_count_limit: number of services that should be waiting for each set of unique constraints before
|
||||
## attempting to spawn a node
|
||||
## - service_wait_time_limit: number of seconds a service should be waiting before attempting to spawn a node
|
||||
## - node_idle_limit: number of seconds a node should be idle before terminating it
|
||||
## - slots_min_limit: minimum number of worker slots that should be started and active
|
||||
## - slots_min_spare: number of spare (unused) worker slots that should be started and active
|
||||
# service_wait_count_limit: 0
|
||||
# service_wait_time_limit: 5
|
||||
# node_idle_limit: 120
|
||||
# slots_min_limit: 0
|
||||
# slots_min_spare: 0
|
||||
# Program output will be written to the log
|
||||
#log_file: '{xdg_data_home}/galaxy_swarm_manager.log'
|
||||
|
||||
# Each set of limits can also be set on a per-constraint basis under the `limits` list. Each member of `limits` is a
|
||||
# dictionary with at least the key `constraints`, whose value is a list of constraint strings matching constraint
|
||||
# strings that jobs will be created with, either by the use of `service_create_*_constraint` or manually setting
|
||||
# constraints. Other keys match the `node_*` and `slots_*` limits above. Any limits unset will default to the "global"
|
||||
# limits as set above, or their defaults. For example:
|
||||
# limits:
|
||||
# - constraints:
|
||||
# - node.labels._galaxy_image==bgruening/docker-jupyter-notebook:16.01.1
|
||||
# - node.labels._galaxy_cpus==1
|
||||
# slots_min_limit: 2
|
||||
# slots_min_spare: 1
|
||||
# Level of log messages (levels are Python logging module level names (case insensitive))
|
||||
#log_level: INFO
|
||||
|
||||
# Amount of time to wait for a spawning node to appear in `docker node ls` before considering it failed
|
||||
# spawn_wait_time: 30
|
||||
# Log message format
|
||||
#log_format: %(name)s %(levelname)s %(asctime)s %(message)s
|
||||
|
||||
## Command to run to spawn new nodes. This command should join the node to the swarm. It is run once per unique set
|
||||
## of constraints of waiting services. Can include template variables:
|
||||
## - {cpus}: Number of CPUs needed by the requested service(s) or minimum limits
|
||||
## - {slots}: Number of "slots" needed by the requested service(s) or minimum limits, where slots are CPU fractions
|
||||
#i# determined by use of the "cpus" option in the `docker_swarm` section.
|
||||
## - {image}: Image requested by service
|
||||
## - {service_ids}: Comma-separated list of service ids with these constraints currently waiting
|
||||
## - {service_count}: Number of services with these constraints currently waiting
|
||||
## If this command does not block until the node is joined to the swarm, make sure it at least completes that step in
|
||||
## `spawn_wait_time` once it returns control. How the `spawn_command` exits controls the swarm manager's behavior:
|
||||
## - return code: 0, output: space-separated list of spawned nodes:
|
||||
## Swarm manager will wait for the nodes to appear in the swarm and manage them. You are responsible for ensuring
|
||||
## the names returned match the name as it will appear in the output of `docker node ls`.
|
||||
## - return code: 0, output: empty:
|
||||
## Refusing to allocate nodes and the swarm manager should not attempt to spawn nodes for the given service(s)
|
||||
## again.
|
||||
## - return code: 2, output: anything (it will be logged as a message)
|
||||
## Refusing to allocate nodes and the swarm manager should attempt to spawn nodes for the given service(s) again.
|
||||
## This is useful for controlling the maximum number of nodes that will be spawned.
|
||||
# spawn_command: /bin/true
|
||||
# List of services' environment variables that should be logged when performing periodic service logging. By
|
||||
# default, none are logged, but `USER_EMAIL` is useful for GIE containers to log the user who is running the
|
||||
# container.
|
||||
#log_environment_variables:
|
||||
# - USER_EMAIL
|
||||
|
||||
## Command to run to destroy idle nodes. Can include template variables:
|
||||
## - {nodes}: Space-separated list of node names to destroy
|
||||
## This command should block until at least the point at which any nodes being destroyed no longer appear in
|
||||
## `docker node ls`. It should return the names of nodes destroyed, separated by spaces.
|
||||
# destroy_command: /bin/true
|
||||
# Command to run to spawn new nodes. This command should join the node to the swarm. It is run once per
|
||||
# unique set of constraints of waiting services. Can include template variables:
|
||||
#
|
||||
# - {cpus}: Number of CPUs needed by the requested service(s) or minimum limits
|
||||
# - {slots}: Number of "slots" needed by the requested service(s) or minimum limits, where slots are CPU
|
||||
# fractions determined by use of the "cpus" option in the `docker_swarm` section.
|
||||
# - {image}: Image requested by service
|
||||
# - {service_ids}: Comma-separated list of service ids with these constraints currently waiting
|
||||
# - {service_count}: Number of services with these constraints currently waiting
|
||||
#
|
||||
# If this command does not block until the node is joined to the swarm, make sure it at least completes that
|
||||
# step in `spawn_wait_time` once it returns control. How the `spawn_command` exits controls the swarm
|
||||
# manager's behavior:
|
||||
#
|
||||
# - return code: 0, output: space-separated list of spawned nodes:
|
||||
# Swarm manager will wait for the nodes to appear in the swarm and manage them. You are responsible for ensuring
|
||||
# the names returned match the name as it will appear in the output of `docker node ls`.
|
||||
# - return code: 0, output: empty:
|
||||
# Refusing to allocate nodes and the swarm manager should not attempt to spawn nodes for the given service(s)
|
||||
# again. This is useful for controlling the maximum number of nodes that will be spawned.
|
||||
# - return code: 2, output: anything (it will be logged as a message)
|
||||
# Refusing/failed to allocate nodes but the swarm manager should attempt to spawn nodes for the given
|
||||
# service(s) again.
|
||||
#
|
||||
#spawn_command: /bin/true
|
||||
|
||||
## Command to run if either of the above commands failed (e.g. to notify an administrator). Can include template
|
||||
## variables:
|
||||
## - {failed_command}: Command line of the command that failed
|
||||
# command_failure_command: /bin/true
|
||||
# Command to run to destroy idle nodes. Can include template variables:
|
||||
#
|
||||
# - {nodes}: Space-separated list of node names to destroy
|
||||
#
|
||||
# This command should block until at least the point at which any nodes being destroyed no longer appear in
|
||||
# the swarm. It should return the names of nodes destroyed, separated by spaces.
|
||||
#
|
||||
#destroy_command: /bin/true
|
||||
|
||||
## Number of times to retry spawn/destroy commands before considering them to have failed, and seconds to wait between
|
||||
## retries
|
||||
# command_retries: 0
|
||||
# command_retry_wait: 10
|
||||
# Command to run if either of the above commands failed (e.g. to notify an administrator). Can include
|
||||
# template variables:
|
||||
#
|
||||
# - {failed_command}: Command line of the command that failed
|
||||
#
|
||||
#command_failure_command: /bin/true
|
||||
|
||||
## Stop the swarm manager daemon when there are no services or nodes to manage
|
||||
# terminate_when_idle: yes
|
||||
# Number of times to retry spawn/destroy commands before considering them to have failed, and seconds to
|
||||
# wait between retries.
|
||||
#command_retries: 0
|
||||
#command_retry_wait: 10
|
||||
|
||||
# Amount of time to wait for a spawning node to appear in the swarm before considering it failed
|
||||
#spawn_wait_time: 30
|
||||
|
||||
# Stop the swarm manager daemon when there are no services or nodes to manage
|
||||
#terminate_when_idle: yes
|
||||
|
||||
#
|
||||
# Limits control when the swarm manager will spawn and terminate nodes.
|
||||
#
|
||||
|
||||
# Number of services that must be waiting to run before attempting to spawn a node. If using constraints,
|
||||
# then waiting services are grouped by constraint.
|
||||
#service_wait_count_limit: 0
|
||||
|
||||
# Number of seconds a service must be waiting before attempting to spawn a node.
|
||||
#service_wait_time_limit: 5
|
||||
|
||||
# Number of seconds a node must be idle before terminating it.
|
||||
#node_idle_limit: 120
|
||||
|
||||
# Minimum number of worker slots that should be started and active.
|
||||
#slots_min_limit: 0
|
||||
|
||||
# Number of spare (unused) worker slots that should be started and active
|
||||
#slots_min_spare: 0
|
||||
|
||||
#
|
||||
# Each set of limits can also be set on a per-constraint basis under the `limits` list. Each member of
|
||||
# `limits` is a dictionary dictionary with at least the key `constraints`, whose value is a list of
|
||||
# constraint strings matching constraint strings that jobs will be created with, either by the use of
|
||||
# `service_create_*_constraint` or manually setting constraints. Other keys match the `node_*` and `slots_*`
|
||||
# limits above. Any limits unset will default to the "global" limits as set above, or their defaults.
|
||||
#
|
||||
# If you are using constraints, you should at least define them in the limits section, even if using all
|
||||
# "global" values for the limits. This allows the swarm manager to know what node types to keep around if
|
||||
# you're using `slots_min_limit`.
|
||||
#
|
||||
|
||||
# Limits section example:
|
||||
#limits:
|
||||
# constraints:
|
||||
# - node.labels._galaxy_image==bgruening/docker-jupyter-notebook:16.01.1
|
||||
# - node.labels._galaxy_cpus==1
|
||||
# slots_min_limit: 2
|
||||
# slots_min_spare: 1
|
||||
|
||||
@@ -49,6 +49,11 @@ class DockerInterface(ContainerInterface):
|
||||
'cpus': None,
|
||||
'memory': None,
|
||||
}
|
||||
# These values are inserted into kwopts for run commands
|
||||
conf_run_kwopts = (
|
||||
'cpus',
|
||||
'memory',
|
||||
)
|
||||
|
||||
@property
|
||||
def _default_image(self):
|
||||
@@ -56,8 +61,7 @@ class DockerInterface(ContainerInterface):
|
||||
return self._conf.image
|
||||
|
||||
def run_in_container(self, command, image=None, **kwopts):
|
||||
# FIXME: these containers_conf overrides should be defined as class vars
|
||||
for opt in ('cpus', 'memory'):
|
||||
for opt in self.conf_run_kwopts:
|
||||
if self._conf[opt]:
|
||||
kwopts[opt] = self._conf[opt]
|
||||
self.set_kwopts_name(kwopts)
|
||||
@@ -231,8 +235,9 @@ class DockerAPIClient(object):
|
||||
if r:
|
||||
log.warning("The request appears to have succeeded, will not retry. Response is: %s", str(r))
|
||||
return r
|
||||
elif max_tries > 0 and tries > max_tries:
|
||||
elif max_tries > 0 and tries >= max_tries:
|
||||
log.error("Maximum number of attempts (%s) exceeded", max_tries)
|
||||
return r
|
||||
else:
|
||||
tries += 1
|
||||
of = " of %s" % max_tries if max_tries > 0 else ""
|
||||
@@ -312,6 +317,10 @@ class DockerAPIInterface(DockerInterface):
|
||||
|
||||
@staticmethod
|
||||
def _kwopt_to_param_names(map_spec, key):
|
||||
"""For a given containers lib method parameter name, return the matching docker-py parameter name(s).
|
||||
|
||||
See :meth:`_create_docker_api_spec`.
|
||||
"""
|
||||
params = []
|
||||
if 'param' not in map_spec and 'params' not in map_spec:
|
||||
params.append(key)
|
||||
@@ -322,6 +331,11 @@ class DockerAPIInterface(DockerInterface):
|
||||
|
||||
@staticmethod
|
||||
def _kwopt_to_params(map_spec, key, value):
|
||||
"""For a given containers lib method parameter name and value, return the matching docker-py parameters with
|
||||
values set (including transformation with an optional map function).
|
||||
|
||||
See :meth:`_create_docker_api_spec`.
|
||||
"""
|
||||
params = {}
|
||||
if 'map' in map_spec:
|
||||
value = map_spec['map'](value)
|
||||
@@ -330,54 +344,108 @@ class DockerAPIInterface(DockerInterface):
|
||||
return params
|
||||
|
||||
def _create_docker_api_spec(self, option_map_name, spec_class, kwopts):
|
||||
"""Creates docker-py objects used as arguments to API methods.
|
||||
"""Create docker-py objects used as arguments to docker-py methods.
|
||||
|
||||
This method modifies ``kwopts`` by removing options that match the spec.
|
||||
|
||||
An option map is a class-level variable with name ``<map_name>_option_map`` and is a dict with format:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
sample_option_map = {
|
||||
'containers_lib_option_name': {
|
||||
'param': docker_lib_positional_argument_int or 'docker_lib_keyword_argument_name',
|
||||
'params': like 'param' but an iterable containing multiple docker lib params to set,
|
||||
'default': default value,
|
||||
'map': function with with to transform the value,
|
||||
'required': True if this param is required, else False (default),
|
||||
},
|
||||
'_spec_param': {
|
||||
'spec_class': class of param value,
|
||||
}
|
||||
}
|
||||
|
||||
All members of the mapping value are optional.
|
||||
|
||||
For example, a spec map for (some of) the possible values of the :class:`docker.types.TaskTemplate`, which is
|
||||
used as the ``task_template`` argument to :meth:`docker.APIClient.create_service`, and the possible values of
|
||||
the :class`:docker.types.ContainerSpec`, which is used as the ``container_spec`` argument to the
|
||||
``TaskTemplate`` would be:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
task_template_option_map = {
|
||||
# TaskTemplate's 'container_spec' param is a ContainerSpec
|
||||
'_container_spec': {
|
||||
'spec_class': docker.types.ContainerSpec,
|
||||
'required': True
|
||||
}
|
||||
}
|
||||
container_spec_option_map = {
|
||||
'image': {'param': 0}, # positional argument 0 to ContainerSpec()
|
||||
'command': {}, # 'command' keyword argument to ContainerSpec()
|
||||
'environment': { # 'env' keyword argument to ContainerSpec(), 'environment' keyword argument
|
||||
'param': 'env' # to ContainerInterface.run_in_container()
|
||||
},
|
||||
}
|
||||
|
||||
Thus, calling ``DockerInterface.run_in_contaner('true', image='busybox', environment={'FOO': 'foo'}`` will
|
||||
essentially do this (for example, if using Docker Swarm mode):
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
container_spec = docker.types.ContainerSpec('busybox', command='true', env={'FOO': 'foo'})
|
||||
task_template = docker.types.TaskTemplate(container_spec=container_spec)
|
||||
docker.APIClient().create_service(task_template)
|
||||
|
||||
:param option_map_name: Name of option map class variable (``_option_map`` is automatically appended)
|
||||
:type option_map_name: str
|
||||
:param spec_class: docker-py specification class or callable returning an instance
|
||||
:type spec_class: :class:`docker.types.Resources`, :class:`docker.types.ContainerSpec`, etc. or
|
||||
callable
|
||||
:param kwopts: Keyword options passed to calling method (e.g. :method:`DockerInterface.run()`)
|
||||
:param kwopts: Keyword options passed to calling method (e.g.
|
||||
:meth:`DockerInterface.run_in_container`)
|
||||
:type kwopts: dict
|
||||
:returns: Instantiated ``spec_class`` object
|
||||
:rtype: ``type(spec_class)``
|
||||
"""
|
||||
def _kwopt_to_arg(map_spec, key, value, param=None):
|
||||
# determines whether the given param is a positional or keyword argument in docker-py and adds it to the
|
||||
# list of arguments
|
||||
if isinstance(map_spec.get('param'), int):
|
||||
spec_opts.append((map_spec.get('param'), value))
|
||||
elif param is not None:
|
||||
spec_kwopts[param] = value
|
||||
else:
|
||||
spec_kwopts.update(DockerAPIInterface._kwopt_to_params(map_spec, key, value))
|
||||
# TODO: make this cleaner
|
||||
# positional arguments
|
||||
spec_opts = []
|
||||
# keyword arguments
|
||||
spec_kwopts = {}
|
||||
# retrieve the option map for the docker-py object we're creating
|
||||
option_map = getattr(self, option_map_name + '_option_map')
|
||||
# set defaults
|
||||
for key in filter(lambda k: option_map[k].get('default'), option_map.keys()):
|
||||
map_spec = option_map[key]
|
||||
_kwopt_to_arg(map_spec, key, map_spec['default'])
|
||||
# don't allow kwopts that start with _, those are reserved for "child" classes
|
||||
# don't allow kwopts that start with _, those are reserved for "child" object params
|
||||
for kwopt in filter(lambda k: not k.startswith('_') and k in option_map, kwopts.keys()):
|
||||
map_spec = option_map[kwopt]
|
||||
_v = kwopts.pop(kwopt)
|
||||
_kwopt_to_arg(map_spec, kwopt, _v)
|
||||
# look for any child classes that need to be checked
|
||||
# find any child objects that need to be created and recurse to create them
|
||||
for _sub_k in filter(lambda k: k.startswith('_') and 'spec_class' in option_map[k], option_map.keys()):
|
||||
map_spec = option_map[_sub_k]
|
||||
param = _sub_k.lstrip('_')
|
||||
_sub_v = self._create_docker_api_spec(param, map_spec['spec_class'], kwopts)
|
||||
if _sub_v is not None or map_spec.get('required') or isinstance(map_spec.get('param'), int):
|
||||
_kwopt_to_arg(map_spec, None, _sub_v, param=param)
|
||||
# override params with values defined in the config
|
||||
for key in filter(lambda k: self._conf.get(k) is not None, option_map.keys()):
|
||||
_kwopt_to_arg(map_spec, key, self._conf[key])
|
||||
# sort positional args and make into a flat tuple
|
||||
if spec_opts:
|
||||
spec_opts = sorted(spec_opts, key=lambda x: x[0])
|
||||
spec_opts = [i[1] for i in spec_opts]
|
||||
if spec_kwopts:
|
||||
# create spec object
|
||||
if spec_opts or spec_kwopts:
|
||||
return spec_class(*spec_opts, **spec_kwopts)
|
||||
else:
|
||||
return None
|
||||
|
||||
@@ -365,7 +365,6 @@ class DockerSwarmAPIInterface(DockerSwarmInterface, DockerAPIInterface):
|
||||
'image': {'param': 0},
|
||||
'command': {},
|
||||
'environment': {'param': 'env'},
|
||||
# FIXME: is our param name labels?
|
||||
'labels': {},
|
||||
}
|
||||
restart_policy_option_map = {
|
||||
@@ -375,6 +374,7 @@ class DockerSwarmAPIInterface(DockerSwarmInterface, DockerAPIInterface):
|
||||
}
|
||||
task_template_option_map = {
|
||||
'_container_spec': {'spec_class': docker.types.ContainerSpec, 'required': True},
|
||||
'_resources': {'spec_class': docker.types.Resources},
|
||||
'_restart_policy': {'spec_class': docker.types.RestartPolicy},
|
||||
'_placement': {'spec_class': docker.types.Placement},
|
||||
}
|
||||
@@ -398,19 +398,25 @@ class DockerSwarmAPIInterface(DockerSwarmInterface, DockerAPIInterface):
|
||||
#
|
||||
|
||||
def service_create(self, command, image=None, **kwopts):
|
||||
# TODO: some of this should probably move to run_in_container when the CLI interface is removed
|
||||
log.debug("Creating docker service with image '%s' for command: %s", image, command)
|
||||
# FIXME: these should be class level vars
|
||||
if ('service_create_image_constraint' in self._conf or 'service_create_cpus_constraint' in self._conf) and 'constraint' not in kwopts:
|
||||
kwopts['constraint'] = []
|
||||
# insert run kwopts from config
|
||||
for opt in self.conf_run_kwopts:
|
||||
if self._conf[opt]:
|
||||
kwopts[opt] = self._conf[opt]
|
||||
# image is part of the container spec
|
||||
kwopts['image'] = self._get_image(image)
|
||||
# service constraints
|
||||
kwopts['constraint'] = kwopts.get('constraint', [])
|
||||
if self._conf.service_create_image_constraint:
|
||||
kwopts['constraint'].append((IMAGE_CONSTRAINT, '==', image))
|
||||
kwopts['constraint'].append((IMAGE_CONSTRAINT + '==' + image))
|
||||
if self._conf.service_create_cpus_constraint:
|
||||
cpus = kwopts.get('reserve_cpus', kwopts.get('limit_cpus', '1'))
|
||||
kwopts['constraint'].append((CPUS_CONSTRAINT, '==', cpus))
|
||||
kwopts['constraint'].append((CPUS_CONSTRAINT + '==' + cpus))
|
||||
# ports
|
||||
if 'publish_port_random' in kwopts:
|
||||
kwopts['ports'] = [DockerSwarmAPIInterface.create_random_port_spec(kwopts.pop('publish_port_random'))]
|
||||
# create specs
|
||||
service_mode = self._create_docker_api_spec('service_mode', docker.types.ServiceMode, kwopts)
|
||||
endpoint_spec = self._create_docker_api_spec('endpoint_spec', docker.types.EndpointSpec, kwopts)
|
||||
task_template = self._create_docker_api_spec('task_template', docker.types.TaskTemplate, kwopts)
|
||||
@@ -464,7 +470,7 @@ class DockerSwarmAPIInterface(DockerSwarmInterface, DockerAPIInterface):
|
||||
spec = node.inspect['Spec']
|
||||
if 'label_add' in kwopts:
|
||||
kwopts['labels'] = spec.get('Labels', {})
|
||||
kwopts['labels'].update(kwopts.pop('labels_add'))
|
||||
kwopts['labels'].update(kwopts.pop('label_add'))
|
||||
spec.update(self._create_docker_api_spec('node_spec', dict, kwopts))
|
||||
return self._client.update_node(node.id, node.version, node_spec=spec)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user