compose/fig/cli/main.py

259 lines
7.4 KiB
Python
Raw Normal View History

2013-12-11 15:25:32 +01:00
import logging
2013-12-19 14:06:26 +01:00
import sys
2013-12-11 15:25:32 +01:00
import re
from inspect import getdoc
from .. import __version__
from ..project import NoSuchService
2013-12-11 15:25:32 +01:00
from .command import Command
2013-12-19 14:02:04 +01:00
from .formatter import Formatter
2013-12-18 15:58:58 +01:00
from .log_printer import LogPrinter
2013-12-31 14:42:58 +01:00
from .utils import yesno
2013-12-11 15:25:32 +01:00
from docker.client import APIError
2013-12-11 15:25:32 +01:00
from .errors import UserError
from .docopt_command import NoSuchCommand
2013-12-20 16:03:01 +01:00
from .socketclient import SocketClient
2013-12-11 15:25:32 +01:00
log = logging.getLogger(__name__)
2013-12-13 20:19:44 +01:00
2013-12-11 15:25:32 +01:00
def main():
2013-12-13 20:19:44 +01:00
console_handler = logging.StreamHandler()
console_handler.setFormatter(logging.Formatter())
console_handler.setLevel(logging.INFO)
root_logger = logging.getLogger()
root_logger.addHandler(console_handler)
root_logger.setLevel(logging.DEBUG)
# Disable requests logging
logging.getLogger("requests").propagate = False
2013-12-11 15:25:32 +01:00
try:
command = TopLevelCommand()
command.sys_dispatch()
except KeyboardInterrupt:
log.error("\nAborting.")
exit(1)
except UserError, e:
log.error(e.msg)
exit(1)
except NoSuchService, e:
log.error(e.msg)
exit(1)
2013-12-11 15:25:32 +01:00
except NoSuchCommand, e:
log.error("No such command: %s", e.command)
log.error("")
log.error("\n".join(parse_doc_section("commands:", getdoc(e.supercommand))))
exit(1)
except APIError, e:
log.error(e.explanation)
exit(1)
2013-12-11 15:25:32 +01:00
# stolen from docopt master
def parse_doc_section(name, source):
pattern = re.compile('^([^\n]*' + name + '[^\n]*\n?(?:[ \t].*?(?:\n|$))*)',
re.IGNORECASE | re.MULTILINE)
return [s.strip() for s in pattern.findall(source)]
class TopLevelCommand(Command):
2013-12-20 22:34:27 +01:00
"""Punctual, lightweight development environments using Docker.
2013-12-11 15:25:32 +01:00
Usage:
2013-12-20 21:28:24 +01:00
fig [options] [COMMAND] [ARGS...]
fig -h|--help
2013-12-11 15:25:32 +01:00
Options:
--verbose Show more output
--version Print version and exit
Commands:
2013-12-31 18:05:20 +01:00
kill Kill containers
2013-12-20 11:57:28 +01:00
logs View output from containers
2013-12-20 20:15:12 +01:00
ps List containers
2013-12-31 18:05:20 +01:00
rm Remove stopped containers
2013-12-13 21:55:28 +01:00
run Run a one-off command
2013-12-13 21:35:54 +01:00
start Start services
stop Stop services
2013-12-31 18:05:20 +01:00
up Create and start containers
2013-12-11 15:25:32 +01:00
"""
2013-12-19 14:06:26 +01:00
def docopt_options(self):
options = super(TopLevelCommand, self).docopt_options()
2013-12-20 21:28:24 +01:00
options['version'] = "fig %s" % __version__
2013-12-19 14:06:26 +01:00
return options
2013-12-31 18:05:20 +01:00
def kill(self, options):
"""
Kill containers.
Usage: kill [SERVICE...]
"""
self.project.kill(service_names=options['SERVICE'])
def logs(self, options):
"""
View output from containers.
Usage: logs [SERVICE...]
"""
containers = self.project.containers(service_names=options['SERVICE'], stopped=False)
print "Attaching to", list_containers(containers)
LogPrinter(containers, attach_params={'logs': True}).run()
2013-12-11 15:25:32 +01:00
def ps(self, options):
"""
2013-12-20 20:15:12 +01:00
List containers.
2013-12-11 15:25:32 +01:00
2013-12-20 20:13:55 +01:00
Usage: ps [options] [SERVICE...]
2013-12-19 14:02:04 +01:00
Options:
-q Only display IDs
2013-12-11 15:25:32 +01:00
"""
2013-12-20 20:13:55 +01:00
containers = self.project.containers(service_names=options['SERVICE'], stopped=True) + self.project.containers(service_names=options['SERVICE'], one_off=True)
2013-12-19 14:02:04 +01:00
if options['-q']:
for container in containers:
2013-12-19 14:02:04 +01:00
print container.id
else:
headers = [
'Name',
'Command',
'State',
'Ports',
]
rows = []
for container in containers:
2013-12-19 14:02:04 +01:00
rows.append([
container.name,
container.human_readable_command,
container.human_readable_state,
container.human_readable_ports,
])
print Formatter().table(headers, rows)
2013-12-11 15:25:32 +01:00
2013-12-31 18:05:20 +01:00
def rm(self, options):
"""
Remove stopped containers
Usage: rm [SERVICE...]
"""
all_containers = self.project.containers(service_names=options['SERVICE'], stopped=True)
stopped_containers = [c for c in all_containers if not c.is_running]
if len(stopped_containers) > 0:
print "Going to remove", list_containers(stopped_containers)
if yesno("Are you sure? [yN] ", default=False):
self.project.remove_stopped(service_names=options['SERVICE'])
else:
print "No stopped containers"
2013-12-13 21:55:28 +01:00
def run(self, options):
"""
Run a one-off command.
2013-12-20 11:53:07 +01:00
Usage: run [options] SERVICE COMMAND [ARGS...]
Options:
-d Detached mode: Run container in the background, print new container name
2013-12-13 21:55:28 +01:00
"""
service = self.project.get_service(options['SERVICE'])
2013-12-17 15:13:12 +01:00
container_options = {
'command': [options['COMMAND']] + options['ARGS'],
2013-12-20 16:03:01 +01:00
'tty': not options['-d'],
'stdin_open': not options['-d'],
2013-12-17 15:13:12 +01:00
}
container = service.create_container(one_off=True, **container_options)
2013-12-20 11:53:07 +01:00
if options['-d']:
service.start_container(container, ports=None)
print container.name
else:
2013-12-20 16:03:01 +01:00
with self._attach_to_container(
container.id,
interactive=True,
logs=True,
raw=True
) as c:
service.start_container(container, ports=None)
c.run()
2013-12-13 21:55:28 +01:00
2013-12-31 18:05:20 +01:00
def start(self, options):
"""
Start existing containers.
Usage: start [SERVICE...]
"""
self.project.start(service_names=options['SERVICE'])
def stop(self, options):
"""
Stop running containers.
Usage: stop [SERVICE...]
"""
self.project.stop(service_names=options['SERVICE'])
def up(self, options):
2013-12-11 15:25:32 +01:00
"""
2013-12-20 20:15:12 +01:00
Create and start containers.
2013-12-11 15:25:32 +01:00
Usage: up [options] [SERVICE...]
2013-12-20 11:53:07 +01:00
Options:
-d Detached mode: Run containers in the background, print new container names
2013-12-11 15:25:32 +01:00
"""
detached = options['-d']
self.project.create_containers(service_names=options['SERVICE'])
containers = self.project.containers(service_names=options['SERVICE'], stopped=True)
if not detached:
print "Attaching to", list_containers(containers)
log_printer = LogPrinter(containers)
self.project.start(service_names=options['SERVICE'])
if not detached:
try:
log_printer.run()
finally:
self.project.kill(service_names=options['SERVICE'])
2013-12-20 16:03:01 +01:00
def _attach_to_container(self, container_id, interactive, logs=False, stream=True, raw=False):
stdio = self.client.attach_socket(
container_id,
params={
'stdin': 1 if interactive else 0,
'stdout': 1,
'stderr': 0,
'logs': 1 if logs else 0,
'stream': 1 if stream else 0
},
ws=True,
)
stderr = self.client.attach_socket(
container_id,
params={
'stdin': 0,
'stdout': 0,
'stderr': 1,
'logs': 1 if logs else 0,
'stream': 1 if stream else 0
},
ws=True,
)
return SocketClient(
socket_in=stdio,
socket_out=stdio,
socket_err=stderr,
raw=raw,
)
def list_containers(containers):
return ", ".join(c.name for c in containers)