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鈥檒l occasionally send you account related emails.

Already on GitHub? Sign in to your account

Advanced config file #1233

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
14 changes: 8 additions & 6 deletions compose/cli/command.py
Expand Up @@ -71,14 +71,15 @@ def get_client(self, verbose=False):

def get_project(self, config_path, project_name=None, verbose=False):
try:
config_dict = config.load(config_path)
return Project.from_dicts(
self.get_project_name(config_path, project_name),
config.load(config_path),
self.get_project_name(config_dict, project_name),
config_dict,
self.get_client(verbose=verbose))
except ConfigError as e:
raise errors.UserError(six.text_type(e))

def get_project_name(self, config_path, project_name=None):
def get_project_name(self, config_dict, project_name=None):
def normalize_name(name):
return re.sub(r'[^a-z0-9]', '', name.lower())

Expand All @@ -90,9 +91,10 @@ def normalize_name(name):
if project_name is not None:
return normalize_name(project_name)

project = os.path.basename(os.path.dirname(os.path.abspath(config_path)))
if project:
return normalize_name(project)
if config_dict:
project_name = config_dict['project']
if project_name:
return normalize_name(project_name)

return 'default'

Expand Down
64 changes: 56 additions & 8 deletions compose/config.py
@@ -1,6 +1,8 @@
import os
import yaml
import six
import compose
from distutils.version import StrictVersion


DOCKER_CONFIG_KEYS = [
Expand Down Expand Up @@ -56,22 +58,68 @@ def load(filename):


def from_dictionary(dictionary, working_dir=None, filename=None):
service_dicts = []

for service_name, service_dict in list(dictionary.items()):
if not isinstance(service_dict, dict):
raise ConfigurationError('Service "%s" doesn\'t have any configuration options. All top level keys in your docker-compose.yml must map to a dictionary of configuration options.' % service_name)
loader = ServiceLoader(working_dir=working_dir, filename=filename)
service_dict = loader.make_service_dict(service_name, service_dict)
service_dicts.append(service_dict)
if not isinstance(dictionary, dict) or 'version' not in dictionary or not isinstance(dictionary['version'], (basestring, int, float)):
dictionary = {
'version': 1.0,
'services': dictionary
}

return service_dicts
loader = ConfigLoader(working_dir=working_dir, filename=filename)
config_dict = loader.make_config_dict(dictionary)
return config_dict


def make_service_dict(name, service_dict, working_dir=None):
return ServiceLoader(working_dir=working_dir).make_service_dict(name, service_dict)


class ConfigLoader(object):
def __init__(self, working_dir, filename=None):
self.working_dir = working_dir
self.filename = filename
self.service_loader = ServiceLoader(working_dir=self.working_dir, filename=self.filename)

def make_config_dict(self, config_dict):
if 'version' not in config_dict:
raise ConfigurationError('The configuration option "version" is compulsory. You docker-compose.yml must have a top level key "version"')

try:
schema_version = StrictVersion(str(config_dict['version']))
except:
raise ConfigurationError('The configuration version "%s" is not a valid version.' % config_dict['version'])

if schema_version > StrictVersion(compose.__version__):
raise ConfigurationError('The configuration version "%s" is not compatible with the current version of docker-compose "%s".' % (config_dict['version'], compose.__version__))

config = {
'version': str(schema_version),
'project': None if self.filename is None else os.path.basename(os.path.dirname(os.path.abspath(self.filename)))
}

if schema_version >= StrictVersion('1.0'):
if 'services' not in config_dict:
raise ConfigurationError('The configuration option "services" is compulsory. You docker-compose.yml must have a top level key "services"')

if not isinstance(config_dict['services'], dict):
raise ConfigurationError('The configuration option "services" is not a valide dictionnary.')

config['services'] = []
for service_name, service_dict in list(config_dict['services'].items()):
if not isinstance(service_dict, dict):
raise ConfigurationError('Service "%s" doesn\'t have any configuration options. All keys defined in the section service in your docker-compose.yml must map to a config_dict of configuration options.' % service_name)
service_dict = self.service_loader.make_service_dict(service_name, service_dict)
config['services'].append(service_dict)

if schema_version >= StrictVersion('1.1'):
if 'project' in config_dict:
if not isinstance(config_dict['project'], basestring):
raise ConfigurationError('The configuration option "project" must be a valid string')
config['project'] = str(config_dict['project'])

return config


class ServiceLoader(object):
def __init__(self, working_dir, filename=None, already_seen=None):
self.working_dir = working_dir
Expand Down
4 changes: 2 additions & 2 deletions compose/project.py
Expand Up @@ -61,12 +61,12 @@ def __init__(self, name, services, client):
self.client = client

@classmethod
def from_dicts(cls, name, service_dicts, client):
def from_dicts(cls, name, config_dict, client):
"""
Construct a ServiceCollection from a list of dicts representing services.
"""
project = cls(name, [], client)
for service_dict in sort_service_dicts(service_dicts):
for service_dict in sort_service_dicts(config_dict['services']):
links = project.get_links(service_dict)
volumes_from = project.get_volumes_from(service_dict)
net = project.get_net(service_dict)
Expand Down
3 changes: 2 additions & 1 deletion docs/cli.md
Expand Up @@ -140,7 +140,8 @@ By default, if there are existing containers for a service, `docker-compose up`

### -p, --project-name NAME

Specifies an alternate project name (default: current directory name)
Specifies an alternate project name (default: when defined, used the option
project in `docker-compose.yml` else use current directory name)


## Environment Variables
Expand Down
24 changes: 13 additions & 11 deletions docs/django.md
Expand Up @@ -43,17 +43,19 @@ describes the services that comprise your app (here, a web server and database),
which Docker images they use, how they link together, what volumes will be
mounted inside the containers, and what ports they expose.

db:
image: postgres
web:
build: .
command: python manage.py runserver 0.0.0.0:8000
volumes:
- .:/code
ports:
- "8000:8000"
links:
- db
version: 1.0
services:
db:
image: postgres
web:
build: .
command: python manage.py runserver 0.0.0.0:8000
volumes:
- .:/code
ports:
- "8000:8000"
links:
- db

See the [`docker-compose.yml` reference](yml.html) for more information on how
this file works.
Expand Down
42 changes: 23 additions & 19 deletions docs/index.md
Expand Up @@ -31,14 +31,16 @@ Next, you define the services that make up your app in `docker-compose.yml` so
they can be run together in an isolated environment:

```yaml
web:
build: .
links:
- db
ports:
- "8000:8000"
db:
image: postgres
version: 1.0
services:
web:
build: .
links:
- db
ports:
- "8000:8000"
db:
image: postgres
```

Lastly, run `docker-compose up` and Compose will start and run your entire app.
Expand Down Expand Up @@ -120,17 +122,19 @@ and the

Next, define a set of services using `docker-compose.yml`:

web:
build: .
command: python app.py
ports:
- "5000:5000"
volumes:
- .:/code
links:
- redis
redis:
image: redis
version: 1.0
services:
web:
build: .
command: python app.py
ports:
- "5000:5000"
volumes:
- .:/code
links:
- redis
redis:
image: redis

This defines two services:

Expand Down
28 changes: 15 additions & 13 deletions docs/rails.md
Expand Up @@ -33,19 +33,21 @@ Next, create a bootstrap `Gemfile` which just loads Rails. It'll be overwritten

Finally, `docker-compose.yml` is where the magic happens. This file describes the services that comprise your app (a database and a web app), how to get each one's Docker image (the database just runs on a pre-made PostgreSQL image, and the web app is built from the current directory), and the configuration needed to link them together and expose the web app's port.

db:
image: postgres
ports:
- "5432"
web:
build: .
command: bundle exec rails s -p 3000 -b '0.0.0.0'
volumes:
- .:/myapp
ports:
- "3000:3000"
links:
- db
version: 1.0
services:
db:
image: postgres
ports:
- "5432"
web:
build: .
command: bundle exec rails s -p 3000 -b '0.0.0.0'
volumes:
- .:/myapp
ports:
- "3000:3000"
links:
- db

### Build the project

Expand Down
28 changes: 15 additions & 13 deletions docs/wordpress.md
Expand Up @@ -37,19 +37,21 @@ Next you'll create a `docker-compose.yml` file that will start your web service
and a separate MySQL instance:

```
web:
build: .
command: php -S 0.0.0.0:8000 -t /code
ports:
- "8000:8000"
links:
- db
volumes:
- .:/code
db:
image: orchardup/mysql
environment:
MYSQL_DATABASE: wordpress
version: 1.0
services:
web:
build: .
command: php -S 0.0.0.0:8000 -t /code
ports:
- "8000:8000"
links:
- db
volumes:
- .:/code
db:
image: orchardup/mysql
environment:
MYSQL_DATABASE: wordpress
```

Two supporting files are needed to get this working - first, `wp-config.php` is
Expand Down