Add a new fig command for retrieving the locally bound port of a service.

Signed-off-by: Daniel Nephin <dnephin@gmail.com>
This commit is contained in:
Daniel Nephin 2014-08-08 09:41:52 -07:00 committed by Daniel Nephin
commit c48ee5caef
9 changed files with 164 additions and 51 deletions

View file

@ -9,6 +9,8 @@ class UserError(Exception):
def __unicode__(self):
return self.msg
__str__ = __unicode__
class DockerNotFoundMac(UserError):
def __init__(self):

View file

@ -84,6 +84,7 @@ class TopLevelCommand(Command):
help Get help on a command
kill Kill containers
logs View output from containers
port Print the public port for a port binding
ps List containers
rm Remove stopped containers
run Run a one-off command
@ -148,6 +149,26 @@ class TopLevelCommand(Command):
print("Attaching to", list_containers(containers))
LogPrinter(containers, attach_params={'logs': True}, monochrome=monochrome).run()
def port(self, project, options):
"""
Print the public port for a port binding.
Usage: port [options] SERVICE PRIVATE_PORT
Options:
--protocol=proto tcp or udp (defaults to tcp)
--index=index index of the container if there are multiple
instances of a service (defaults to 1)
"""
service = project.get_service(options['SERVICE'])
try:
container = service.get_container(number=options.get('--index') or 1)
except ValueError as e:
raise UserError(str(e))
print(container.get_local_port(
options['PRIVATE_PORT'],
protocol=options.get('--protocol') or 'tcp') or '')
def ps(self, project, options):
"""
List containers.

View file

@ -1,6 +1,8 @@
from __future__ import unicode_literals
from __future__ import absolute_import
from fig.packages import six
class Container(object):
"""
@ -63,17 +65,20 @@ class Container(object):
return None
@property
def human_readable_ports(self):
def ports(self):
self.inspect_if_not_inspected()
if not self.dictionary['NetworkSettings']['Ports']:
return ''
ports = []
for private, public in list(self.dictionary['NetworkSettings']['Ports'].items()):
if public:
ports.append('%s->%s' % (public[0]['HostPort'], private))
else:
ports.append(private)
return ', '.join(ports)
return self.dictionary['NetworkSettings']['Ports'] or {}
@property
def human_readable_ports(self):
def format_port(private, public):
if not public:
return private
return '{HostIp}:{HostPort}->{private}'.format(
private=private, **public[0])
return ', '.join(format_port(*item)
for item in sorted(six.iteritems(self.ports)))
@property
def human_readable_state(self):
@ -105,6 +110,10 @@ class Container(object):
self.inspect_if_not_inspected()
return self.dictionary['State']['Running']
def get_local_port(self, port, protocol='tcp'):
port = self.ports.get("%s/%s" % (port, protocol))
return "{HostIp}:{HostPort}".format(**port[0]) if port else None
def start(self, **options):
return self.client.start(self.id, **options)

View file

@ -78,9 +78,22 @@ class Service(object):
name = get_container_name(container)
if not name or not is_valid_name(name, one_off):
return False
project, name, number = parse_name(name)
project, name, _number = parse_name(name)
return project == self.project and name == self.name
def get_container(self, number=1):
"""Return a :class:`fig.container.Container` for this service. The
container must be active, and match `number`.
"""
for container in self.client.containers():
if not self.has_container(container):
continue
_, _, container_number = parse_name(get_container_name(container))
if container_number == number:
return Container.from_ps(self.client, container)
raise ValueError("No container found for %s_%s" % (self.name, number))
def start(self, **options):
for c in self.containers(stopped=True):
self.start_container_if_stopped(c, **options)