Add working dir, config files and env file in service labels

Signed-off-by: Guillaume Rose <guillaume.rose@docker.com>
This commit is contained in:
Guillaume Rose 2019-10-14 14:55:41 +02:00
parent 1678a4fbe4
commit dbe4d7323e
4 changed files with 63 additions and 32 deletions

View File

@ -13,6 +13,9 @@ from .. import config
from .. import parallel from .. import parallel
from ..config.environment import Environment from ..config.environment import Environment
from ..const import API_VERSIONS from ..const import API_VERSIONS
from ..const import LABEL_CONFIG_FILES
from ..const import LABEL_ENVIRONMENT_FILE
from ..const import LABEL_WORKING_DIR
from ..project import Project from ..project import Project
from .docker_client import docker_client from .docker_client import docker_client
from .docker_client import get_tls_version from .docker_client import get_tls_version
@ -57,7 +60,8 @@ def project_from_options(project_dir, options, additional_options={}):
environment=environment, environment=environment,
override_dir=override_dir, override_dir=override_dir,
compatibility=options.get('--compatibility'), compatibility=options.get('--compatibility'),
interpolate=(not additional_options.get('--no-interpolate')) interpolate=(not additional_options.get('--no-interpolate')),
environment_file=environment_file
) )
@ -125,7 +129,7 @@ def get_client(environment, verbose=False, version=None, tls_config=None, host=N
def get_project(project_dir, config_path=None, project_name=None, verbose=False, def get_project(project_dir, config_path=None, project_name=None, verbose=False,
host=None, tls_config=None, environment=None, override_dir=None, host=None, tls_config=None, environment=None, override_dir=None,
compatibility=False, interpolate=True): compatibility=False, interpolate=True, environment_file=None):
if not environment: if not environment:
environment = Environment.from_env_file(project_dir) environment = Environment.from_env_file(project_dir)
config_details = config.find(project_dir, config_path, environment, override_dir) config_details = config.find(project_dir, config_path, environment, override_dir)
@ -145,10 +149,30 @@ def get_project(project_dir, config_path=None, project_name=None, verbose=False,
with errors.handle_connection_errors(client): with errors.handle_connection_errors(client):
return Project.from_config( return Project.from_config(
project_name, config_data, client, environment.get('DOCKER_DEFAULT_PLATFORM') project_name,
config_data,
client,
environment.get('DOCKER_DEFAULT_PLATFORM'),
execution_context_labels(config_details, environment_file),
) )
def execution_context_labels(config_details, environment_file):
extra_labels = [
'{0}={1}'.format(LABEL_WORKING_DIR, os.path.abspath(config_details.working_dir)),
'{0}={1}'.format(LABEL_CONFIG_FILES, config_files_label(config_details)),
]
if environment_file is not None:
extra_labels.append('{0}={1}'.format(LABEL_ENVIRONMENT_FILE,
os.path.normpath(environment_file)))
return extra_labels
def config_files_label(config_details):
return ",".join(
map(str, (os.path.normpath(c.filename) for c in config_details.config_files)))
def get_project_name(working_dir, project_name=None, environment=None): def get_project_name(working_dir, project_name=None, environment=None):
def normalize_name(name): def normalize_name(name):
return re.sub(r'[^-_a-z0-9]', '', name.lower()) return re.sub(r'[^-_a-z0-9]', '', name.lower())

View File

@ -11,6 +11,9 @@ IS_WINDOWS_PLATFORM = (sys.platform == "win32")
LABEL_CONTAINER_NUMBER = 'com.docker.compose.container-number' LABEL_CONTAINER_NUMBER = 'com.docker.compose.container-number'
LABEL_ONE_OFF = 'com.docker.compose.oneoff' LABEL_ONE_OFF = 'com.docker.compose.oneoff'
LABEL_PROJECT = 'com.docker.compose.project' LABEL_PROJECT = 'com.docker.compose.project'
LABEL_WORKING_DIR = 'com.docker.compose.project.working_dir'
LABEL_CONFIG_FILES = 'com.docker.compose.project.config_files'
LABEL_ENVIRONMENT_FILE = 'com.docker.compose.project.environment_file'
LABEL_SERVICE = 'com.docker.compose.service' LABEL_SERVICE = 'com.docker.compose.service'
LABEL_NETWORK = 'com.docker.compose.network' LABEL_NETWORK = 'com.docker.compose.network'
LABEL_VERSION = 'com.docker.compose.version' LABEL_VERSION = 'com.docker.compose.version'

View File

@ -83,7 +83,7 @@ class Project(object):
return labels return labels
@classmethod @classmethod
def from_config(cls, name, config_data, client, default_platform=None): def from_config(cls, name, config_data, client, default_platform=None, extra_labels=[]):
""" """
Construct a Project from a config.Config object. Construct a Project from a config.Config object.
""" """
@ -136,6 +136,7 @@ class Project(object):
pid_mode=pid_mode, pid_mode=pid_mode,
platform=service_dict.pop('platform', None), platform=service_dict.pop('platform', None),
default_platform=default_platform, default_platform=default_platform,
extra_labels=extra_labels,
**service_dict) **service_dict)
) )

View File

@ -68,7 +68,6 @@ else:
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
HOST_CONFIG_KEYS = [ HOST_CONFIG_KEYS = [
'cap_add', 'cap_add',
'cap_drop', 'cap_drop',
@ -137,7 +136,6 @@ class NoSuchImageError(Exception):
ServiceName = namedtuple('ServiceName', 'project service number') ServiceName = namedtuple('ServiceName', 'project service number')
ConvergencePlan = namedtuple('ConvergencePlan', 'action containers') ConvergencePlan = namedtuple('ConvergencePlan', 'action containers')
@ -173,20 +171,21 @@ class BuildAction(enum.Enum):
class Service(object): class Service(object):
def __init__( def __init__(
self, self,
name, name,
client=None, client=None,
project='default', project='default',
use_networking=False, use_networking=False,
links=None, links=None,
volumes_from=None, volumes_from=None,
network_mode=None, network_mode=None,
networks=None, networks=None,
secrets=None, secrets=None,
scale=1, scale=1,
pid_mode=None, pid_mode=None,
default_platform=None, default_platform=None,
**options extra_labels=[],
**options
): ):
self.name = name self.name = name
self.client = client self.client = client
@ -201,6 +200,7 @@ class Service(object):
self.scale_num = scale self.scale_num = scale
self.default_platform = default_platform self.default_platform = default_platform
self.options = options self.options = options
self.extra_labels = extra_labels
def __repr__(self): def __repr__(self):
return '<Service: {}>'.format(self.name) return '<Service: {}>'.format(self.name)
@ -215,7 +215,7 @@ class Service(object):
for container in self.client.containers( for container in self.client.containers(
all=stopped, all=stopped,
filters=filters)]) filters=filters)])
) )
if result: if result:
return result return result
@ -404,8 +404,8 @@ class Service(object):
return ConvergencePlan('start', containers) return ConvergencePlan('start', containers)
if ( if (
strategy is ConvergenceStrategy.always or strategy is ConvergenceStrategy.always or
self._containers_have_diverged(containers) self._containers_have_diverged(containers)
): ):
return ConvergencePlan('recreate', containers) return ConvergencePlan('recreate', containers)
@ -482,6 +482,7 @@ class Service(object):
container, timeout=timeout, attach_logs=not detached, container, timeout=timeout, attach_logs=not detached,
start_new_container=start, renew_anonymous_volumes=renew_anonymous_volumes start_new_container=start, renew_anonymous_volumes=renew_anonymous_volumes
) )
containers, errors = parallel_execute( containers, errors = parallel_execute(
containers, containers,
recreate, recreate,
@ -705,11 +706,11 @@ class Service(object):
net_name = self.network_mode.service_name net_name = self.network_mode.service_name
pid_namespace = self.pid_mode.service_name pid_namespace = self.pid_mode.service_name
return ( return (
self.get_linked_service_names() + self.get_linked_service_names() +
self.get_volumes_from_names() + self.get_volumes_from_names() +
([net_name] if net_name else []) + ([net_name] if net_name else []) +
([pid_namespace] if pid_namespace else []) + ([pid_namespace] if pid_namespace else []) +
list(self.options.get('depends_on', {}).keys()) list(self.options.get('depends_on', {}).keys())
) )
def get_dependency_configs(self): def get_dependency_configs(self):
@ -899,7 +900,7 @@ class Service(object):
container_options['labels'] = build_container_labels( container_options['labels'] = build_container_labels(
container_options.get('labels', {}), container_options.get('labels', {}),
self.labels(one_off=one_off), self.labels(one_off=one_off) + self.extra_labels,
number, number,
self.config_hash if add_config_hash else None, self.config_hash if add_config_hash else None,
slug slug
@ -1552,9 +1553,9 @@ def warn_on_masked_volume(volumes_option, container_volumes, service):
for volume in volumes_option: for volume in volumes_option:
if ( if (
volume.external and volume.external and
volume.internal in container_volumes and volume.internal in container_volumes and
container_volumes.get(volume.internal) != volume.external container_volumes.get(volume.internal) != volume.external
): ):
log.warning(( log.warning((
"Service \"{service}\" is using volume \"{volume}\" from the " "Service \"{service}\" is using volume \"{volume}\" from the "
@ -1601,6 +1602,7 @@ def build_mount(mount_spec):
read_only=mount_spec.read_only, consistency=mount_spec.consistency, **kwargs read_only=mount_spec.read_only, consistency=mount_spec.consistency, **kwargs
) )
# Labels # Labels
@ -1655,6 +1657,7 @@ def format_environment(environment):
if isinstance(value, six.binary_type): if isinstance(value, six.binary_type):
value = value.decode('utf-8') value = value.decode('utf-8')
return '{key}={value}'.format(key=key, value=value) return '{key}={value}'.format(key=key, value=value)
return [format_env(*item) for item in environment.items()] return [format_env(*item) for item in environment.items()]