mirror of
https://github.com/docker/compose.git
synced 2025-04-08 17:05:13 +02:00
Remove Environment singleton, instead carry instance during config processing
Project name and compose file detection also updated Signed-off-by: Joffrey F <joffrey@docker.com>
This commit is contained in:
parent
bf8e501b5e
commit
b9ca5188a2
@ -34,7 +34,7 @@ def get_config_path_from_options(base_dir, options):
|
||||
if file_option:
|
||||
return file_option
|
||||
|
||||
environment = config.environment.get_instance(base_dir)
|
||||
environment = config.environment.Environment(base_dir)
|
||||
config_files = environment.get('COMPOSE_FILE')
|
||||
if config_files:
|
||||
return config_files.split(os.pathsep)
|
||||
@ -58,7 +58,7 @@ def get_project(project_dir, config_path=None, project_name=None, verbose=False,
|
||||
config_details = config.find(project_dir, config_path)
|
||||
project_name = get_project_name(config_details.working_dir, project_name)
|
||||
config_data = config.load(config_details)
|
||||
environment = config.environment.get_instance(project_dir)
|
||||
environment = config.environment.Environment(project_dir)
|
||||
|
||||
api_version = environment.get(
|
||||
'COMPOSE_API_VERSION',
|
||||
@ -75,7 +75,7 @@ def get_project_name(working_dir, project_name=None):
|
||||
def normalize_name(name):
|
||||
return re.sub(r'[^a-z0-9]', '', name.lower())
|
||||
|
||||
environment = config.environment.get_instance(working_dir)
|
||||
environment = config.environment.Environment(working_dir)
|
||||
project_name = project_name or environment.get('COMPOSE_PROJECT_NAME')
|
||||
if project_name:
|
||||
return normalize_name(project_name)
|
||||
|
@ -114,14 +114,24 @@ DEFAULT_OVERRIDE_FILENAME = 'docker-compose.override.yml'
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class ConfigDetails(namedtuple('_ConfigDetails', 'working_dir config_files')):
|
||||
class ConfigDetails(namedtuple('_ConfigDetails', 'working_dir config_files environment')):
|
||||
"""
|
||||
:param working_dir: the directory to use for relative paths in the config
|
||||
:type working_dir: string
|
||||
:param config_files: list of configuration files to load
|
||||
:type config_files: list of :class:`ConfigFile`
|
||||
:param environment: computed environment values for this project
|
||||
:type environment: :class:`environment.Environment`
|
||||
"""
|
||||
|
||||
def __new__(cls, working_dir, config_files):
|
||||
return super(ConfigDetails, cls).__new__(
|
||||
cls,
|
||||
working_dir,
|
||||
config_files,
|
||||
Environment(working_dir),
|
||||
)
|
||||
|
||||
|
||||
class ConfigFile(namedtuple('_ConfigFile', 'filename config')):
|
||||
"""
|
||||
@ -291,12 +301,8 @@ def load(config_details):
|
||||
"""
|
||||
validate_config_version(config_details.config_files)
|
||||
|
||||
# load environment in working dir for later use in interpolation
|
||||
# it is done here to avoid having to pass down working_dir
|
||||
Environment.get_instance(config_details.working_dir)
|
||||
|
||||
processed_files = [
|
||||
process_config_file(config_file)
|
||||
process_config_file(config_file, config_details.environment)
|
||||
for config_file in config_details.config_files
|
||||
]
|
||||
config_details = config_details._replace(config_files=processed_files)
|
||||
@ -362,7 +368,7 @@ def load_services(config_details, config_file):
|
||||
service_name,
|
||||
service_dict)
|
||||
resolver = ServiceExtendsResolver(
|
||||
service_config, config_file
|
||||
service_config, config_file, environment=config_details.environment
|
||||
)
|
||||
service_dict = process_service(resolver.run())
|
||||
|
||||
@ -371,7 +377,8 @@ def load_services(config_details, config_file):
|
||||
service_dict = finalize_service(
|
||||
service_config,
|
||||
service_names,
|
||||
config_file.version)
|
||||
config_file.version,
|
||||
config_details.environment)
|
||||
return service_dict
|
||||
|
||||
def build_services(service_config):
|
||||
@ -402,16 +409,17 @@ def load_services(config_details, config_file):
|
||||
return build_services(service_config)
|
||||
|
||||
|
||||
def interpolate_config_section(filename, config, section):
|
||||
def interpolate_config_section(filename, config, section, environment):
|
||||
validate_config_section(filename, config, section)
|
||||
return interpolate_environment_variables(config, section)
|
||||
return interpolate_environment_variables(config, section, environment)
|
||||
|
||||
|
||||
def process_config_file(config_file, service_name=None):
|
||||
def process_config_file(config_file, environment, service_name=None):
|
||||
services = interpolate_config_section(
|
||||
config_file.filename,
|
||||
config_file.get_service_dicts(),
|
||||
'service')
|
||||
'service',
|
||||
environment,)
|
||||
|
||||
if config_file.version == V2_0:
|
||||
processed_config = dict(config_file.config)
|
||||
@ -419,11 +427,13 @@ def process_config_file(config_file, service_name=None):
|
||||
processed_config['volumes'] = interpolate_config_section(
|
||||
config_file.filename,
|
||||
config_file.get_volumes(),
|
||||
'volume')
|
||||
'volume',
|
||||
environment,)
|
||||
processed_config['networks'] = interpolate_config_section(
|
||||
config_file.filename,
|
||||
config_file.get_networks(),
|
||||
'network')
|
||||
'network',
|
||||
environment,)
|
||||
|
||||
if config_file.version == V1:
|
||||
processed_config = services
|
||||
@ -440,11 +450,12 @@ def process_config_file(config_file, service_name=None):
|
||||
|
||||
|
||||
class ServiceExtendsResolver(object):
|
||||
def __init__(self, service_config, config_file, already_seen=None):
|
||||
def __init__(self, service_config, config_file, environment=None, already_seen=None):
|
||||
self.service_config = service_config
|
||||
self.working_dir = service_config.working_dir
|
||||
self.already_seen = already_seen or []
|
||||
self.config_file = config_file
|
||||
self.environment = environment or Environment(None)
|
||||
|
||||
@property
|
||||
def signature(self):
|
||||
@ -474,7 +485,7 @@ class ServiceExtendsResolver(object):
|
||||
extends_file = ConfigFile.from_filename(config_path)
|
||||
validate_config_version([self.config_file, extends_file])
|
||||
extended_file = process_config_file(
|
||||
extends_file, service_name=service_name
|
||||
extends_file, self.environment, service_name=service_name
|
||||
)
|
||||
service_config = extended_file.get_service(service_name)
|
||||
|
||||
@ -489,6 +500,7 @@ class ServiceExtendsResolver(object):
|
||||
service_dict),
|
||||
self.config_file,
|
||||
already_seen=self.already_seen + [self.signature],
|
||||
environment=self.environment
|
||||
)
|
||||
|
||||
service_config = resolver.run()
|
||||
@ -518,7 +530,7 @@ class ServiceExtendsResolver(object):
|
||||
return filename
|
||||
|
||||
|
||||
def resolve_environment(service_dict):
|
||||
def resolve_environment(service_dict, environment=None):
|
||||
"""Unpack any environment variables from an env_file, if set.
|
||||
Interpolate environment values if set.
|
||||
"""
|
||||
@ -527,12 +539,12 @@ def resolve_environment(service_dict):
|
||||
env.update(env_vars_from_file(env_file))
|
||||
|
||||
env.update(parse_environment(service_dict.get('environment')))
|
||||
return dict(resolve_env_var(k, v) for k, v in six.iteritems(env))
|
||||
return dict(resolve_env_var(k, v, environment) for k, v in six.iteritems(env))
|
||||
|
||||
|
||||
def resolve_build_args(build):
|
||||
def resolve_build_args(build, environment):
|
||||
args = parse_build_arguments(build.get('args'))
|
||||
return dict(resolve_env_var(k, v) for k, v in six.iteritems(args))
|
||||
return dict(resolve_env_var(k, v, environment) for k, v in six.iteritems(args))
|
||||
|
||||
|
||||
def validate_extended_service_dict(service_dict, filename, service):
|
||||
@ -611,11 +623,11 @@ def process_service(service_config):
|
||||
return service_dict
|
||||
|
||||
|
||||
def finalize_service(service_config, service_names, version):
|
||||
def finalize_service(service_config, service_names, version, environment):
|
||||
service_dict = dict(service_config.config)
|
||||
|
||||
if 'environment' in service_dict or 'env_file' in service_dict:
|
||||
service_dict['environment'] = resolve_environment(service_dict)
|
||||
service_dict['environment'] = resolve_environment(service_dict, environment)
|
||||
service_dict.pop('env_file', None)
|
||||
|
||||
if 'volumes_from' in service_dict:
|
||||
@ -642,7 +654,7 @@ def finalize_service(service_config, service_names, version):
|
||||
if 'restart' in service_dict:
|
||||
service_dict['restart'] = parse_restart_spec(service_dict['restart'])
|
||||
|
||||
normalize_build(service_dict, service_config.working_dir)
|
||||
normalize_build(service_dict, service_config.working_dir, environment)
|
||||
|
||||
service_dict['name'] = service_config.name
|
||||
return normalize_v1_service_format(service_dict)
|
||||
@ -836,11 +848,10 @@ def parse_ulimits(ulimits):
|
||||
return dict(ulimits)
|
||||
|
||||
|
||||
def resolve_env_var(key, val):
|
||||
environment = Environment.get_instance()
|
||||
def resolve_env_var(key, val, environment):
|
||||
if val is not None:
|
||||
return key, val
|
||||
elif key in environment:
|
||||
elif environment and key in environment:
|
||||
return key, environment[key]
|
||||
else:
|
||||
return key, None
|
||||
@ -880,7 +891,7 @@ def resolve_volume_path(working_dir, volume):
|
||||
return container_path
|
||||
|
||||
|
||||
def normalize_build(service_dict, working_dir):
|
||||
def normalize_build(service_dict, working_dir, environment):
|
||||
|
||||
if 'build' in service_dict:
|
||||
build = {}
|
||||
@ -890,7 +901,9 @@ def normalize_build(service_dict, working_dir):
|
||||
else:
|
||||
build.update(service_dict['build'])
|
||||
if 'args' in build:
|
||||
build['args'] = build_string_dict(resolve_build_args(build))
|
||||
build['args'] = build_string_dict(
|
||||
resolve_build_args(build, environment)
|
||||
)
|
||||
|
||||
service_dict['build'] = build
|
||||
|
||||
|
@ -29,24 +29,10 @@ class BlankDefaultDict(dict):
|
||||
|
||||
|
||||
class Environment(BlankDefaultDict):
|
||||
__instance = None
|
||||
|
||||
@classmethod
|
||||
def get_instance(cls, base_dir='.'):
|
||||
if cls.__instance:
|
||||
return cls.__instance
|
||||
|
||||
instance = cls(base_dir)
|
||||
cls.__instance = instance
|
||||
return instance
|
||||
|
||||
@classmethod
|
||||
def reset(cls):
|
||||
cls.__instance = None
|
||||
|
||||
def __init__(self, base_dir):
|
||||
super(Environment, self).__init__()
|
||||
self.load_environment_file(os.path.join(base_dir, '.env'))
|
||||
if base_dir:
|
||||
self.load_environment_file(os.path.join(base_dir, '.env'))
|
||||
self.update(os.environ)
|
||||
|
||||
def load_environment_file(self, path):
|
||||
@ -63,7 +49,3 @@ class Environment(BlankDefaultDict):
|
||||
)
|
||||
mapping.__setitem__(*line.split('=', 1))
|
||||
self.update(mapping)
|
||||
|
||||
|
||||
def get_instance(base_dir=None):
|
||||
return Environment.get_instance(base_dir)
|
||||
|
@ -6,17 +6,15 @@ from string import Template
|
||||
|
||||
import six
|
||||
|
||||
from .environment import Environment
|
||||
from .errors import ConfigurationError
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def interpolate_environment_variables(config, section):
|
||||
mapping = Environment.get_instance()
|
||||
def interpolate_environment_variables(config, section, environment):
|
||||
|
||||
def process_item(name, config_dict):
|
||||
return dict(
|
||||
(key, interpolate_value(name, key, val, section, mapping))
|
||||
(key, interpolate_value(name, key, val, section, environment))
|
||||
for key, val in (config_dict or {}).items()
|
||||
)
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user