Skip to content

Commit

Permalink
Implement #745: configurable project name
Browse files Browse the repository at this point in the history
Signed-off-by: Sergey Protko <fesors@gmail.com>
  • Loading branch information
Sergey Protko authored and fesor committed Mar 13, 2016
1 parent 99d68be commit a203ff4
Show file tree
Hide file tree
Showing 11 changed files with 96 additions and 11 deletions.
6 changes: 3 additions & 3 deletions compose/cli/command.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,8 +78,8 @@ def get_client(verbose=False, version=None):

def get_project(base_dir, config_path=None, project_name=None, verbose=False):
config_details = config.find(base_dir, config_path)
project_name = get_project_name(config_details.working_dir, project_name)
config_data = config.load(config_details)
project_name = get_project_name(config_details.working_dir, project_name, config_data.project_name)

api_version = os.environ.get(
'COMPOSE_API_VERSION',
Expand All @@ -89,11 +89,11 @@ def get_project(base_dir, config_path=None, project_name=None, verbose=False):
return Project.from_config(project_name, config_data, client)


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

project_name = project_name or os.environ.get('COMPOSE_PROJECT_NAME')
project_name = project_name or os.environ.get('COMPOSE_PROJECT_NAME') or config_project_name
if project_name:
return normalize_name(project_name)

Expand Down
47 changes: 47 additions & 0 deletions compose/config/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,26 @@ def version(self):

return version

@cached_property
def project_name(self):
if 'project_name' not in self.config:
return None

project_name = self.config['project_name']

if isinstance(project_name, dict):
log.warn('Unexpected type for "project_name" key in "{}". Assuming '
'"project_name" is the name of a service, and defaulting to '
'Compose file version 1.'.format(self.filename))
return None

if not isinstance(project_name, six.string_types):
raise ConfigurationError(
'Project name in "{}" is invalid - it should be a string.'
.format(self.filename))

return self.config['project_name']

def get_service(self, name):
return self.get_service_dicts()[name]

Expand All @@ -192,6 +212,24 @@ class Config(namedtuple('_Config', 'version services volumes networks')):
:type networks: :class:`dict`
"""

project_name = None

@classmethod
def with_project_name(cls, version, project_name, services, volumes, networks):
if not project_name:
raise ValueError("No project name for Config")

config = cls(
version,
services,
volumes,
networks
)

config.project_name = project_name

return config


class ServiceConfig(namedtuple('_ServiceConfig', 'working_dir filename name config')):

Expand Down Expand Up @@ -310,6 +348,15 @@ def load(config_details):
for service_dict in service_dicts:
match_named_volumes(service_dict, volumes)

if main_file.project_name:
return Config.with_project_name(
main_file.version,
main_file.project_name,
service_dicts,
volumes,
networks
)

return Config(main_file.version, service_dicts, volumes, networks)


Expand Down
4 changes: 4 additions & 0 deletions compose/config/config_schema_v2.0.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@
"type": "string"
},

"project_name": {
"type": "string"
},

"services": {
"id": "#/properties/services",
"type": "object",
Expand Down
4 changes: 4 additions & 0 deletions compose/config/serialize.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@ def serialize_config(config):
'networks': config.networks,
'volumes': config.volumes,
}

if config.project_name:
output['project_name'] = config.project_name

return yaml.safe_dump(
output,
default_flow_style=False,
Expand Down
16 changes: 14 additions & 2 deletions docs/compose-file.md
Original file line number Diff line number Diff line change
Expand Up @@ -904,6 +904,18 @@ Example:
redis:
image: redis

## Project name

The default project name is the basename of the project directory.
But you can set it in `docker-compose.yml`, to make sure that all of your team members
will use the same project name by default.

This property is setting default project name, so you can override it by using the
[`-p` command line option](./reference/overview.md) or the
[`COMPOSE_PROJECT_NAME` environment variable](./reference/envvars.md#compose-project-name).

> **Note:** The `project_name` is only supported in the
> [version 2 file format](#version-2).
### Version 2

Expand Down Expand Up @@ -931,9 +943,10 @@ Simple example:
redis:
image: redis

A more extended example, defining volumes and networks:
A more extended example, defining project name, volumes and networks:

version: '2'
project_name: "myapp"
services:
web:
build: .
Expand All @@ -959,7 +972,6 @@ A more extended example, defining volumes and networks:
back-tier:
driver: bridge


### Upgrading

In the majority of cases, moving from version 1 to 2 is a very simple process:
Expand Down
5 changes: 3 additions & 2 deletions docs/faq.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,9 @@ handling `SIGTERM` properly.
Compose uses the project name to create unique identifiers for all of a
project's containers and other resources. To run multiple copies of a project,
set a custom project name using the [`-p` command line
option](./reference/overview.md) or the [`COMPOSE_PROJECT_NAME`
environment variable](./reference/envvars.md#compose-project-name).
option](./reference/overview.md), the [`COMPOSE_PROJECT_NAME`
environment variable](./reference/envvars.md#compose-project-name)
or specify it in [docker-compose.yml](./compose-file.md#project-name).

## What's the difference between `up`, `run`, and `start`?

Expand Down
5 changes: 3 additions & 2 deletions docs/networking.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,9 @@ identical to the container name.
> **Note:** Your app's network is given a name based on the "project name",
> which is based on the name of the directory it lives in. You can override the
> project name with either the [`--project-name`
> flag](reference/overview.md) or the [`COMPOSE_PROJECT_NAME` environment
> variable](reference/envvars.md#compose-project-name).
> flag](reference/overview.md), the [`COMPOSE_PROJECT_NAME` environment
> variable](reference/envvars.md#compose-project-name),
> or specify it in [docker-compose.yml](./compose-file.md#project-name).
For example, suppose your app is in a directory called `myapp`, and your `docker-compose.yml` looks like this:

Expand Down
5 changes: 3 additions & 2 deletions docs/overview.md
Original file line number Diff line number Diff line change
Expand Up @@ -93,8 +93,9 @@ Compose uses a project name to isolate environments from each other. You can mak

The default project name is the basename of the project directory. You can set
a custom project name by using the
[`-p` command line option](./reference/overview.md) or the
[`COMPOSE_PROJECT_NAME` environment variable](./reference/envvars.md#compose-project-name).
[`-p` command line option](./reference/overview.md), the
[`COMPOSE_PROJECT_NAME` environment variable](./reference/envvars.md#compose-project-name)
or specify it in [docker-compose.yml](./compose-file.md#project-name).

### Preserve volume data when containers are created

Expand Down
1 change: 1 addition & 0 deletions tests/acceptance/cli_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,7 @@ def test_config_default(self):
output = yaml.load(result.stdout)
expected = {
'version': '2.0',
'project_name': 'example',
'volumes': {'data': {'driver': 'local'}},
'networks': {'front': {}},
'services': {
Expand Down
2 changes: 2 additions & 0 deletions tests/fixtures/v2-full/docker-compose.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@

version: "2"

project_name: example

volumes:
data:
driver: local
Expand Down
12 changes: 12 additions & 0 deletions tests/unit/cli_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,18 @@ def test_project_name_from_environment_new_var(self):
project_name = get_project_name(None)
self.assertEquals(project_name, name)

def test_project_name_from_environment_new_var_over_config(self):
name = 'namefromenv'
with mock.patch.dict(os.environ):
os.environ['COMPOSE_PROJECT_NAME'] = name
project_name = get_project_name(None, None, 'name-from-config')
self.assertEquals(project_name, name)

def test_project_name_from_config(self):
name = 'namefromconfig'
project_name = get_project_name(None, project_name=None, config_project_name='name-from-config')
self.assertEquals(project_name, name)

def test_project_name_with_empty_environment_var(self):
base_dir = 'tests/fixtures/simple-composefile'
with mock.patch.dict(os.environ):
Expand Down

0 comments on commit a203ff4

Please sign in to comment.