Skip to content

Commit

Permalink
Merge pull request #90 from se7entyse7en/support-aliases
Browse files Browse the repository at this point in the history
Add support for aliases
  • Loading branch information
se7entyse7en authored Oct 20, 2019
2 parents d4d1180 + 9f0673a commit 5e57b4c
Show file tree
Hide file tree
Showing 7 changed files with 64 additions and 24 deletions.
1 change: 1 addition & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ language: python
python:
- "3.6"
- "3.7"
- "3.8"

services:
- docker
Expand Down
1 change: 1 addition & 0 deletions HISTORY.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
### Added

- Added possibility to specify container args for its creation in `.toml` file
- Added support for command aliases to be defined in `.toml` file

## [v0.4.1 - 2019-08-26](https://github.com/se7entyse7en/pydockenv/compare/v0.4.0...v0.4.1)

Expand Down
63 changes: 41 additions & 22 deletions pydockenv/commands/environment.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
import json
import os
from dataclasses import dataclass
from dataclasses import field
from typing import Dict

import click
import toml
Expand All @@ -14,32 +18,46 @@ def get_current_env():
return os.environ.get('PYDOCKENV')


@dataclass(frozen=True)
class EnvironmentConfig:
name: str
python: str = 'latest'
dependencies: Dict[str, str] = field(default_factory=dict)
container_args: Dict[str, str] = field(default_factory=dict)
aliases: Dict[str, Dict[str, str]] = field(default_factory=dict)

@classmethod
def from_file(cls, file_: str) -> 'EnvironmentConfig':
config = toml.load(file_)['tool']['pydockenv']
return EnvironmentConfig(**config)


def create(project_dir, file_, name, version):
if file_:
config = toml.load(file_)['tool']['pydockenv']
name = name or config['name']
version = config['python']
deps = config['dependencies']
container_args = config['container_args']
config = EnvironmentConfig.from_file(file_)
else:
version = version or 'latest'
deps = {}
container_args = {}
config = EnvironmentConfig(name, python=version or 'latest')

create_from_config(project_dir, config)


click.echo(f'Creating environment {name} with python version {version}...')
image_name = f'python:{version}'
def create_from_config(project_dir: str, config: EnvironmentConfig):
click.echo(f'Creating environment {config.name} with python version '
f'{config.python}...')
image_name = f'python:{config.python}'

client = Client.get_instance()
try:
image = client.images.get(image_name)
except docker.errors.ImageNotFound:
click.echo(f'Image {image_name} not found, pulling...')
image = client.images.pull('python', tag=version)
image = client.images.pull('python', tag=config.python)

create_network(name)
create_env(image, name, project_dir, deps, container_args)
create_network(config.name)
create_env(image, project_dir, config)

click.echo(f'Environment {name} with python version {version} created!')
click.echo(f'Environment {config.name} with python version '
f'{config.python} created!')


def status():
Expand Down Expand Up @@ -132,7 +150,7 @@ def delete_network(env_name):
network.remove()


def create_env(image, name, project_dir, deps, container_args):
def create_env(image, project_dir, config):
workdir = os.path.abspath(project_dir)
mounts = [
Mount('/usr/src', workdir, type='bind')
Expand All @@ -142,27 +160,28 @@ def create_env(image, name, project_dir, deps, container_args):
'stdin_open': True,
'labels': {
'workdir': workdir,
'env_name': name,
'env_name': config.name,
'aliases': json.dumps(config.aliases),
},
'name': definitions.CONTAINERS_PREFIX + name,
'name': definitions.CONTAINERS_PREFIX + config.name,
'mounts': mounts,
'network': definitions.CONTAINERS_PREFIX + name + '_network',
'network': definitions.CONTAINERS_PREFIX + config.name + '_network',
}

filtered_container_args = {k: v for k, v in container_args.items()
filtered_container_args = {k: v for k, v in config.container_args.items()
if k not in kwargs}
kwargs.update(filtered_container_args)

container = Client.get_instance().containers.create(image, **kwargs)

if deps:
if config.dependencies:
# TODO: Remove this from here just to avoid circular imports
from pydockenv.commands import dependency

container.start()

click.echo(f'Installing {len(deps)} dependencies...')
packages = [f'{dep}{v}' for dep, v in deps.items()]
click.echo(f'Installing {len(config.dependencies)} dependencies...')
packages = [f'{dep}{v}' for dep, v in config.dependencies.items()]
click.echo(f'Installing {packages}...')
dependency.install_for_container(container, packages, None)

Expand Down
4 changes: 3 additions & 1 deletion pydockenv/commands/io.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

from pydockenv import definitions
from pydockenv.client import Client
from pydockenv.commands.environment import EnvironmentConfig
from pydockenv.commands.environment import create_env
from pydockenv.commands.environment import create_network
from pydockenv.commands.environment import get_current_env
Expand All @@ -19,7 +20,8 @@ def load(name, project_dir, input_file):
image = Client.get_instance().images.load(fin)[0]

create_network(name)
create_env(image, name, project_dir)
config = EnvironmentConfig(name)
create_env(image, project_dir, config)

click.echo(f'Environment {name} loaded from {input_file}!')

Expand Down
9 changes: 9 additions & 0 deletions pydockenv/executor.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import json
import os
import subprocess
from contextlib import contextmanager
Expand Down Expand Up @@ -58,6 +59,14 @@ def execute(cls, *args, **kwargs):
click.echo(f'Container {current_env} not found, exiting...')
raise

if len(args) == 1:
aliases = container.labels.get('aliases')
if aliases:
alias = json.loads(aliases).get(args[0])
if alias is not None:
kwargs['ports'] = alias.get('ports', [])
args = alias['cmd'].split(' ')

return cls.execute_for_container(container, *args, **kwargs)

@classmethod
Expand Down
7 changes: 7 additions & 0 deletions setup.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import re
import sys
from functools import partial

import setuptools
Expand All @@ -9,6 +10,11 @@
'docker>=3.7.0,<3.8.0',
]


if sys.version_info.minor < 7:
requires.append('dataclasses==0.6')


scripts = [
'bin/pydockenv',
]
Expand All @@ -22,6 +28,7 @@
'Programming Language :: Unix Shell',
'Programming Language :: Python :: 3.6',
'Programming Language :: Python :: 3.7',
'Programming Language :: Python :: 3.8',
'Topic :: Software Development',
'Topic :: Utilities'
]
Expand Down
3 changes: 2 additions & 1 deletion tests/integration/test_environment.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,8 @@ def test_create(self):

expected = {
'env_name': env_name,
'workdir': str(Path(self._projs_dir, proj_name))
'workdir': str(Path(self._projs_dir, proj_name)),
'aliases': '{}',
}
actual = r['Config']['Labels']
self.assertEqual(expected, actual)
Expand Down

0 comments on commit 5e57b4c

Please sign in to comment.