diff --git a/podctl/console_script.py b/podctl/console_script.py index da94f5f..cf4ff91 100644 --- a/podctl/console_script.py +++ b/podctl/console_script.py @@ -40,7 +40,11 @@ class ConsoleScript(cli2.ConsoleScript): for name, script in self.podfile.pod.scripts.items(): cb = self.podfile.pod.script(name) cb.__doc__ = inspect.getdoc(script) or script.doc - self[name] = cli2.Callable(name, cb) + self[name] = cli2.Callable( + name, + cb, + options=script.options, + ) return super().__call__(*args, **kwargs) def call(self, command): diff --git a/podctl/pod.py b/podctl/pod.py index 877d175..bbf7028 100644 --- a/podctl/pod.py +++ b/podctl/pod.py @@ -4,16 +4,48 @@ import os from .build import Build from .container import Container +from .exceptions import WrongResult from .script import Script from .visitable import Visitable +class Up(Script): + async def run(self, *args, **kwargs): + for key, value in kwargs.items(): + setattr(self, key, value) + + pod = kwargs.get('pod') + + try: + pod.info = (await self.exec( + 'podman', 'pod', 'inspect', pod.name + )).json + print(f'Pod {pod.name} ready') + except WrongResult: + print(f'Pod {pod.name} creating') + await self.exec( + 'podman', 'pod', 'create', '--name', self.pod.name, + ) + print(f'Pod {pod.name} created') + pod.info = (await self.exec( + 'podman', 'pod', 'inspect', pod.name + )).json + + return await super().run(*args, **kwargs) + + class Pod(Visitable): default_scripts = dict( build=Build(), run=Script('run', 'Run a container command'), + up=Up('up', 'Start the stack'), + test=Script('test', 'Run tests inside containers'), ) + @property + def name(self): + return os.getenv('POD', os.getcwd().split('/')[-1]) + @property def containers(self): return [i for i in self.visitors if type(i) == Container] @@ -22,20 +54,6 @@ class Pod(Visitable): async def cb(*args, **kwargs): asyncio.events.get_event_loop() script = copy.deepcopy(self.scripts[name]) - - if args: - containers = [c for c in self.containers if c.name in args] - else: - containers = self.containers - - procs = [] - for container in containers: - procs.append(script( - container, - *args, - container=container, - pod=self, - **kwargs, - )) - return await asyncio.gather(*procs) + kwargs['pod'] = self + return await script.run(*args, **kwargs) return cb diff --git a/podctl/podfile.py b/podctl/podfile.py index 92bad9f..a0a88c9 100644 --- a/podctl/podfile.py +++ b/podctl/podfile.py @@ -1,4 +1,5 @@ import importlib +import os from .container import Container from .pod import Pod @@ -8,9 +9,14 @@ class Podfile: def __init__(self, pods, containers): self.pods = pods self.containers = containers + if not self.pods: self.pods['pod'] = Pod(*containers.values()) + for pod in self.pods.values(): + for container in pod.containers: + container.pod = pod + @property def pod(self): return self.pods['pod'] diff --git a/podctl/proc.py b/podctl/proc.py index b8842e0..c5a5a54 100644 --- a/podctl/proc.py +++ b/podctl/proc.py @@ -20,12 +20,16 @@ class PrefixStreamProtocol(asyncio.subprocess.SubprocessStreamProtocol): super().__init__(*args, **kwargs) def pipe_data_received(self, fd, data): - if fd in (1, 2): + from .console_script import console_script + debug = console_script.parser.options.get('debug', False) + + if (debug is True or 'out' in str(debug)) and fd in (1, 2): for line in data.split(b'\n'): if not line: continue sys.stdout.buffer.write( self.prefix.encode('utf8') + b' | ' + line + b'\n' + if self.prefix else line + b'\n' ) sys.stdout.flush() super().pipe_data_received(fd, data) @@ -38,26 +42,6 @@ def protocol_factory(prefix): loop=asyncio.events.get_event_loop() ) return _p -''' - -async def proc(args, prefix=None, wait=True, raises=True): - loop = asyncio.events.get_event_loop() - transport, protocol = await loop.subprocess_exec( - protocol_factory(prefix), *args) - proc = asyncio.subprocess.Process(transport, protocol, loop) - - if wait: - stdout, stderr = await proc.communicate() - log['result'] = await proc.wait() - - if raises and log['result']: - raise WrongResult() - - if wait: - return log - - return proc -''' class Proc: @@ -92,7 +76,13 @@ class Proc: if self.called: raise Exception('Already called: ' + self.cmd) - print(f'{self.prefix} | + {self.cmd}') + from .console_script import console_script + debug = console_script.parser.options.get('debug', False) + if debug is True or 'proc' in str(debug): + if self.prefix: + print(f'{self.prefix} | + {self.cmd}') + else: + print(f'+ {self.cmd}') loop = asyncio.events.get_event_loop() transport, protocol = await loop.subprocess_exec( @@ -121,3 +111,8 @@ class Proc: if self.raises and self.proc.returncode: raise WrongResult() return self + + @property + def json(self): + import json + return json.loads(self.out) diff --git a/podctl/script.py b/podctl/script.py index da78475..7ef6cf7 100644 --- a/podctl/script.py +++ b/podctl/script.py @@ -1,22 +1,40 @@ +import asyncio +import cli2 import textwrap from .proc import Proc class Script: + options = [ + cli2.Option( + 'debug', + alias='d', + color=cli2.GREEN, + help=''' + Display debug output. + Supports values: proc,out,visit + ''' + ), + ] + def __init__(self, name=None, doc=None): self.name = name or type(self).__name__.lower() self.doc = doc or 'Custom script' async def exec(self, *args, **kwargs): """Execute a command on the host.""" - kwargs.setdefault('prefix', self.container.name) + if getattr(self, 'container', None): + kwargs.setdefault('prefix', self.container.name) proc = await Proc(*args, **kwargs)() if kwargs.get('wait', True): await proc.wait() return proc async def __call__(self, visitable, *args, **kwargs): + from .console_script import console_script + debug = console_script.parser.options.get('debug', False) + for key, value in kwargs.items(): setattr(self, key, value) @@ -36,12 +54,12 @@ class Script: if not hasattr(visitor, method): continue - print( - visitable.name + ' | ', - type(visitor).__name__, - method, - ' '.join(f'{k}={v}' for k, v in visitor.__dict__.items()) - ) + if debug is True or 'visit' in str(debug): + print( + visitable.name + ' | ', + '.'.join([type(visitor).__name__, method]), + ' '.join(f'{k}={v}' for k, v in visitor.__dict__.items()) + ) result = getattr(visitor, method)(self) if result: try: @@ -49,3 +67,22 @@ class Script: except Exception as e: await clean() raise + + async def run(self, *args, **kwargs): + pod = kwargs.get('pod') + + if args: + containers = [c for c in pod.containers if c.name in args] + else: + containers = pod.containers + + procs = [] + for container in containers: + procs.append(self( + container, + *args, + container=container, + **kwargs, + )) + + return await asyncio.gather(*procs) diff --git a/podctl/visitors/commit.py b/podctl/visitors/commit.py index 0c0ed73..c470469 100644 --- a/podctl/visitors/commit.py +++ b/podctl/visitors/commit.py @@ -1,6 +1,8 @@ import os import subprocess +from ..exceptions import WrongResult + CI_VARS = ( # gitlab 'CI_COMMIT_SHORT_SHA', @@ -85,3 +87,13 @@ class Commit: '--name', script.container.name, ':'.join((self.repo, self.tags[0])), ) + + async def up(self, script): + name = '-'.join([script.pod.name, script.container.name]) + try: + await script.exec('podman', 'inspect', name) + except WrongResult: + await script.exec( + 'podman', 'run', '-d', '--name', name, + ':'.join((self.repo, self.tags[0])), + )