Done - before package

pull/1/head
Christophe Mehay 8 years ago
parent a1926eb153
commit 36dc9ba11c

@ -10,7 +10,7 @@ ADD pyentrypoint /opt/pyentrypoint/
ADD tests /opt/pyentrypoint/tests
ADD tests/entrypoint-config.yml /opt/pyentrypoint/
ADD tests/test_template /tmp/tests
ADD tests/test_template.yml.tpl /tmp/test_template.yml
WORKDIR /opt/pyentrypoint/

@ -10,7 +10,7 @@ ADD pyentrypoint /opt/pyentrypoint/
ADD tests /opt/pyentrypoint/tests
ADD tests/entrypoint-config.yml /opt/pyentrypoint/
ADD tests/test_template /tmp/tests
ADD tests/test_template.yml.tpl /tmp/test_template.yml
WORKDIR /opt/pyentrypoint/

@ -1,7 +1,6 @@
"""
Configuration
"""
import fnmatch
import os
from grp import getgrnam
from io import open
@ -9,104 +8,12 @@ from pwd import getpwnam
from command import Command
from docker_links import DockerLinks
from links import Links
from six import string_types
from six import viewitems
from yaml import load
from yaml import Loader
class Link(object):
"""Link object"""
def __init__(self, ip, env, port, protocol, names):
self.ip = ip
self.env = env
self.port = int(port)
self.protocol = protocol
self.uri = '{protocol}://{ip}:{port}'.format(
protocol=protocol,
ip=ip,
port=port,
)
self.names = tuple(names)
def _filter_name(self, name):
"return true if name match"
return bool(fnmatch.filter(self.names, name))
def _filter_port(self, port):
"return true if port match"
return int(port) == self.port
def _filter_protocol(self, protocol):
"return true if protocol match"
return protocol == self.protocol
def _filter_env(self, env):
"return true if env match"
if isinstance(env, dict):
return viewitems(env) <= viewitems(self.env)
if isinstance(env, list):
return bool([key for key in env if key in self.env])
return str(env) in self.env
class Links(object):
"""Links embeder"""
_conf = None
_def_options = {'single': False,
'required': True}
def __init__(self, config=None):
links = DockerLinks()
if not links or len(links.links()) is 0:
pass
self._links = []
for ip, link in links.links().items():
for port, protocol in link["ports"].items():
self._links.append(Link(ip=ip,
env=link['environment'],
port=int(port),
protocol=protocol['protocol'],
names=link['names']))
self._conf = config
def _get_link(self, name):
config = self._conf[name]
links = self._links
options = dict(self._def_options)
for key, val in config.items():
if key in options:
options[key] = val
continue
links = [link for link in links
if getattr(link, "_filter_{}".format(key))(val)]
if options['required'] and len(links) is 0:
raise Exception("No links was found for {name}".format(name=name))
if options['single'] and len(links) > 1:
raise Exception("Only one link should be provided for {name}"
.format(name=name))
if options['single']:
return links[0]
return tuple(links)
@classmethod
def _add_name(cls, name):
"Add method attribute name"
setattr(cls, name, property(lambda self: self._get_link(name)))
@property
def all(self):
"""all returns tuple of all Link objects"""
return tuple(self._links)
class Config(object):
"""Get entrypoint config"""
@ -193,9 +100,9 @@ class Config(object):
if self._links:
return self._links
if 'links' not in self._config:
self._links = Links()
self._links = Links(links=DockerLinks())
return self._links
self._links = Links(config=self._config['links'])
self._links = Links(config=self._config['links'], links=DockerLinks())
for name in self._config['links']:
self._links._add_name(name)
return self._links

@ -0,0 +1,26 @@
"""
Container object handle a single container link
"""
class Container(object):
"""Container handles a single container link"""
ip = None
environ = None
names = None
links = None
def __init__(self, ip, env, names, links=None):
self.ip = ip
self.environ = env
self.names = names
self.links = self._set_links(links)
def _set_links(self, links):
lst = []
for link in links:
if link.ip == self.ip:
lst.append(link)
self.links = tuple(lst)

@ -7,12 +7,18 @@ import json
import os
import re
from container import Container
from links import Links
class DockerLinks(object):
"List all links and return a dictionnary of ip addresses with \
link names, environment, ports and protocols."
_links = None
_containers = None
def __init__(self):
self._get_links()
self._sort_names()
@ -73,6 +79,27 @@ class DockerLinks(object):
)
)
def to_containers(self):
"Return tuple of Container object"
if self._containers:
return self._containers
ctn = []
links = self.get_links()
for ip, item in self.all_links.items():
ctn.append(Container(ip=ip,
env=item['environment'],
names=item['names'],
links=links))
self._containers = tuple(ctn)
return self._containers
def get_links(self):
"Get all links object"
if self._links:
return self._links.all
self._links = Links(links=self)
return self._links.all
def _find_ports(link_name):
rtn = {}

@ -2,12 +2,14 @@
"""
Smart docker-entrypoint
"""
import os
from subprocess import PIPE
from subprocess import Popen
from sys import argv
from command import Command
from config import Config
from docker_links import DockerLinks
from jinja2 import Environment
from jinja2 import FileSystemLoader
from twiggy import levels
@ -40,7 +42,9 @@ class Entrypoint(object):
with open(template, mode='w') as f:
self.log.info('Applying conf to {}'.format(template))
f.write(temp.render(config=self.config,
links=self.config.links))
links=self.config.links,
env=os.environ,
containers=DockerLinks().to_containers()))
def run_conf_cmd(self, cmd):
self.log.info('run command: {}'.format(cmd))

@ -0,0 +1,97 @@
"""
Link handle a single link to another container, determined by his port
"""
import fnmatch
from six import viewitems
class Link(object):
"""Link object"""
def __init__(self, ip, env, port, protocol, names):
self.ip = ip
self.env = env
self.port = int(port)
self.protocol = protocol
self.uri = '{protocol}://{ip}:{port}'.format(
protocol=protocol,
ip=ip,
port=port,
)
self.names = tuple(names)
def _filter_name(self, name):
"return true if name match"
return bool(fnmatch.filter(self.names, name))
def _filter_port(self, port):
"return true if port match"
return int(port) == self.port
def _filter_protocol(self, protocol):
"return true if protocol match"
return protocol == self.protocol
def _filter_env(self, env):
"return true if env match"
if isinstance(env, dict):
return viewitems(env) <= viewitems(self.env)
if isinstance(env, list):
return bool([key for key in env if key in self.env])
return str(env) in self.env
class Links(object):
"""Links embeder"""
_conf = None
_def_options = {'single': False,
'required': True}
def __init__(self, config=None, links=None):
if not links or len(links.links()) is 0:
pass
self._links = []
for ip, link in links.links().items():
for port, protocol in link["ports"].items():
self._links.append(Link(ip=ip,
env=link['environment'],
port=int(port),
protocol=protocol['protocol'],
names=link['names']))
self._conf = config
def _get_link(self, name):
config = self._conf[name]
links = self._links
options = dict(self._def_options)
for key, val in config.items():
if key in options:
options[key] = val
continue
links = [link for link in links
if getattr(link, "_filter_{}".format(key))(val)]
if options['required'] and len(links) is 0:
raise Exception("No links was found for {name}".format(name=name))
if options['single'] and len(links) > 1:
raise Exception("Only one link should be provided for {name}"
.format(name=name))
if options['single']:
return links[0]
return tuple(links)
@classmethod
def _add_name(cls, name):
"Add method attribute name"
setattr(cls, name, property(lambda self: self._get_link(name)))
@property
def all(self):
"""all returns tuple of all Link objects"""
return tuple(self._links)

@ -0,0 +1,22 @@
PyYAML==3.11
Pygments==2.0.2
argparse==1.4.0
aspy.yaml==0.2.1
blessings==1.6
bpython==0.14.2
cached-property==1.3.0
curtsies==0.1.23
docker-compose==1.5.2
docker-py==1.6.0
dockerpty==0.3.4
docopt==0.6.2
greenlet==0.4.9
jsonschema==2.5.1
nodeenv==0.13.6
ordereddict==1.1
pre-commit==0.7.5
requests==2.7.0
six==1.10.0
texttable==0.8.4
virtualenv==13.1.2
websocket-client==0.35.0

@ -7,7 +7,7 @@ user: 1000
group: 1000
config_files:
- /tmp/tests
- /tmp/test_template.yml
secret_env:
- SSHKEY

@ -1,6 +1,10 @@
# Tests using pytest
import fnmatch
from docker_links import DockerLinks
from entrypoint import Entrypoint
from yaml import load
from yaml import Loader
LINKS = [
'test1',
@ -13,7 +17,6 @@ LINKS = [
def test_all_links():
links = DockerLinks()
all_links = links.links()
print(links.to_json())
assert len(all_links) == 4
for _, item in all_links.items():
@ -70,6 +73,17 @@ def test_entrypoint_links():
assert links.test2_800.port == 800
def test_containers():
links = DockerLinks()
ctns = links.to_containers()
assert len(ctns) == 4
for ctn in ctns:
if 'test1' in ctn.names or 'test3' in ctn.names:
assert ctn.environ['FOO'] == 'bar'
def test_templates():
entry = Entrypoint()
@ -78,4 +92,25 @@ def test_templates():
entry.apply_conf()
with open(conf.config_files[0], mode='r') as r:
print(r.read())
test = load(stream=r, Loader=Loader)
assert len(set(test['All links'])) == 4
assert len(set(test['All links 1'])) == 2
assert len(set(test['All links 2'])) == 2
assert fnmatch.fnmatch(test['Links 2 800'][0], 'udp://*:800')
# test environment
assert test['All environ']['FOO'] == 'bar'
assert test['All links 2 environ']['FOO'] == 'bar'
test_names = [
'test1',
'test2',
'test3',
'test4',
]
# test names
for test_name in test_names:
assert test_name in test['All names']

@ -1,17 +0,0 @@
All links:
{% for link in links.all %}
- {{link.uri}}
{% endfor %}
All links 1:
{% for link in links.test1 %}
- {{link.uri}}
{% endfor %}
Links 2 800:
- {{links.test2_800.uri}}
All links 2:
{% for link in links.test2 %}
- {{link.uri}}
{% endfor %}

@ -0,0 +1,39 @@
All links:
{% for link in links.all %}
- {{link.uri}}
{% endfor %}
All links 1:
{% for link in links.test1 %}
- {{link.uri}}
{% endfor %}
Links 2 800:
- {{links.test2_800.uri}}
All links 2:
{% for link in links.test2 %}
- {{link.uri}}
{% endfor %}
All environ:
{% for link in links.all %}
{% for key in link.env %}
{{key}}: {{link.env[key]}}
{% endfor %}
{% endfor %}
All links 2 environ:
{% for link in links.test2 %}
{% for key in link.env %}
{{key}}: {{link.env[key]}}
{% endfor %}
{% endfor %}
All names:
{% for container in containers %}
{% for name in container.names %}
- {{name}}
{% endfor %}
{% endfor %}
Loading…
Cancel
Save