Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

A number is a valid service name #1855

Merged
merged 1 commit into from
Aug 20, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 19 additions & 8 deletions compose/config/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,11 @@
CircularReference,
ComposeFileNotFound,
)
from .validation import validate_against_schema
from .validation import (
validate_against_schema,
validate_service_names,
validate_top_level_object
)


DOCKER_CONFIG_KEYS = [
Expand Down Expand Up @@ -122,19 +126,26 @@ def get_config_path(base_dir):
return os.path.join(path, winner)


@validate_top_level_object
@validate_service_names
def pre_process_config(config):
"""
Pre validation checks and processing of the config file to interpolate env
vars returning a config dict ready to be tested against the schema.
"""
config = interpolate_environment_variables(config)
return config


def load(config_details):
config, working_dir, filename = config_details
if not isinstance(config, dict):
raise ConfigurationError(
"Top level object needs to be a dictionary. Check your .yml file that you have defined a service at the top level."
)

config = interpolate_environment_variables(config)
validate_against_schema(config)
processed_config = pre_process_config(config)
validate_against_schema(processed_config)

service_dicts = []

for service_name, service_dict in list(config.items()):
for service_name, service_dict in list(processed_config.items()):
loader = ServiceLoader(working_dir=working_dir, filename=filename)
service_dict = loader.make_service_dict(service_name, service_dict)
validate_paths(service_dict)
Expand Down
24 changes: 24 additions & 0 deletions compose/config/validation.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from functools import wraps
import os

from docker.utils.ports import split_port
Expand Down Expand Up @@ -36,6 +37,29 @@ def format_ports(instance):
return True


def validate_service_names(func):
@wraps(func)
def func_wrapper(config):
for service_name in config.keys():
if type(service_name) is int:
raise ConfigurationError(
"Service name: {} needs to be a string, eg '{}'".format(service_name, service_name)
)
return func(config)
return func_wrapper


def validate_top_level_object(func):
@wraps(func)
def func_wrapper(config):
if not isinstance(config, dict):
raise ConfigurationError(
"Top level object needs to be a dictionary. Check your .yml file that you have defined a service at the top level."
)
return func(config)
return func_wrapper


def get_unsupported_config_msg(service_name, error_key):
msg = "Unsupported config option for '{}' service: '{}'".format(service_name, error_key)
if error_key in DOCKER_CONFIG_HINTS:
Expand Down
11 changes: 11 additions & 0 deletions tests/unit/config_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,17 @@ def test_config_invalid_service_names(self):
)
)

def test_config_integer_service_name_raise_validation_error(self):
expected_error_msg = "Service name: 1 needs to be a string, eg '1'"
with self.assertRaisesRegexp(ConfigurationError, expected_error_msg):
config.load(
config.ConfigDetails(
{1: {'image': 'busybox'}},
'working_dir',
'filename.yml'
)
)

def test_config_valid_service_names(self):
for valid_name in ['_', '-', '.__.', '_what-up.', 'what_.up----', 'whatup']:
config.load(
Expand Down