Now to rewrite the test framework and good to go !

This commit is contained in:
jpic 2020-02-12 19:34:30 +01:00
parent 2e9a23eb5a
commit 3413fd8981
13 changed files with 212 additions and 82 deletions

View File

@ -22,11 +22,11 @@ class Build(Script):
async def config(self, line):
"""Run buildah config."""
return await self.append(f'buildah config {line} {self.ctr}')
return await self.exec(f'buildah config {line} {self.ctr}')
async def copy(self, src, dst):
"""Run buildah copy to copy a file from host into container."""
return await self.append(f'buildah copy {self.ctr} {src} {dst}')
return await self.exec(f'buildah copy {self.ctr} {src} {dst}')
async def cexec(self, *args, user=None, **kwargs):
"""Execute a command in the container."""
@ -75,3 +75,6 @@ class Build(Script):
p = os.path.join(self.mnt, path[1:], c)
if os.path.exists(p):
return p[len(str(self.mnt)):]
def __repr__(self):
return f'Build'

View File

@ -33,6 +33,10 @@ class ConsoleScript(cli2.ConsoleScript):
self.funckwargs['cmd'] = self.forward_args
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.options = dict()
def __call__(self, *args, **kwargs):
import inspect
from podctl.podfile import Podfile
@ -43,18 +47,22 @@ class ConsoleScript(cli2.ConsoleScript):
self[name] = cli2.Callable(
name,
cb,
options=script.options,
options={o.name: o for o in script.options},
color=getattr(script, 'color', cli2.YELLOW),
)
return super().__call__(*args, **kwargs)
def call(self, command):
self.options = self.parser.options
try:
return super().call(command)
except Mistake as e:
print(e)
sys.exit(1)
except WrongResult:
sys.exit(1)
except WrongResult as e:
print(e)
sys.exit(e.proc.rc)
console_script = ConsoleScript(__doc__).add_module('podctl.console_script')

View File

@ -1,5 +1,53 @@
from .build import Build
from .exceptions import WrongResult
from .visitable import Visitable
class Container(Visitable):
default_scripts = dict(
build=Build(),
)
@property
def container_name(self):
return '-'.join([self.pod.name, self.name])
async def down(self, script):
try:
await script.exec('podman', 'inspect', self.container_name)
except WrongResult:
pass
else:
try:
from podctl.console_script import console_script
argv = console_script.parser.nonoptions
except AttributeError:
argv = []
argv = argv + [self.container_name]
await script.exec('podman', 'rm', '-f', *argv)
async def run(self, script):
await script.exec(
'podman', 'run',
'--name', self.container_name,
':'.join((self.variable('repo'), self.variable('tags')[0])),
)
async def up(self, script):
try:
await script.exec('podman', 'inspect', self.container_name)
except WrongResult as ee:
tag = ':'.join((
self.variable('repo'),
self.variable('tags')[0],
))
print(f'{self.name} | Container creating')
await script.exec(
'podman', 'run', '-d', '--name', self.container_name,
tag,
)
print(f'{self.name} | Container created')
else:
print(f'{self.name} | Container starting')
await script.exec('podman', 'start', self.container_name)
print(f'{self.name} | Container started')

View File

@ -7,4 +7,11 @@ class Mistake(PodctlException):
class WrongResult(PodctlException):
pass
def __init__(self, proc):
self.proc = proc
super().__init__('\n'.join([i for i in [
f'Command failed ! Exit with {proc.rc}'
'+ ' + proc.cmd,
proc.out,
proc.err,
]]))

View File

@ -1,47 +1,44 @@
import asyncio
import copy
import os
from .build import Build
from .container import Container
from .exceptions import WrongResult
from .script import Script
from .scripts import *
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'),
down=Down('down', 'Destroy the stack'),
run=Run('run', 'Run a command in container(s)'),
name=Name(
'name',
'Output the pod name for usage with podman',
),
)
async def down(self, script):
try:
await script.exec('podman', 'pod', 'inspect', self.name)
except WrongResult:
pass
else:
await script.exec('podman', 'pod', 'rm', self.name)
async def up(self, script):
try:
await script.exec(
'podman', 'pod', 'inspect', self.name
)
print(f'{self.name} | Pod ready')
except WrongResult:
print(f'{self.name} | Pod creating')
await script.exec(
'podman', 'pod', 'create', '--name', self.name,
)
print(f'{self.name} | Pod created')
@property
def name(self):
return os.getenv('POD', os.getcwd().split('/')[-1])
@ -53,7 +50,9 @@ class Pod(Visitable):
def script(self, name):
async def cb(*args, **kwargs):
asyncio.events.get_event_loop()
script = copy.deepcopy(self.scripts[name])
kwargs['pod'] = self
return await script.run(*args, **kwargs)
return await self.scripts[name].run(*args, **kwargs)
return cb
def __repr__(self):
return self.name

View File

@ -21,7 +21,7 @@ class PrefixStreamProtocol(asyncio.subprocess.SubprocessStreamProtocol):
def pipe_data_received(self, fd, data):
from .console_script import console_script
debug = console_script.parser.options.get('debug', False)
debug = console_script.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'):
@ -34,6 +34,7 @@ class PrefixStreamProtocol(asyncio.subprocess.SubprocessStreamProtocol):
sys.stdout.flush()
super().pipe_data_received(fd, data)
def protocol_factory(prefix):
def _p():
return PrefixStreamProtocol(
@ -77,8 +78,8 @@ class Proc:
raise Exception('Already called: ' + 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):
debug = console_script.options.get('debug', False)
if debug is True or 'cmd' in str(debug):
if self.prefix:
print(f'{self.prefix} | + {self.cmd}')
else:
@ -109,7 +110,7 @@ class Proc:
if not self.communicated:
await self.communicate()
if self.raises and self.proc.returncode:
raise WrongResult()
raise WrongResult(self)
return self
@property

View File

@ -1,4 +1,5 @@
import asyncio
import copy
import cli2
import textwrap
@ -11,10 +12,11 @@ class Script:
'debug',
alias='d',
color=cli2.GREEN,
default='visit',
help='''
Display debug output.
Supports values: proc,out,visit
'''
Display debug output. Supports values (combinable): cmd,out,visit
'''.strip(),
immediate=True,
),
]
@ -24,7 +26,7 @@ class Script:
async def exec(self, *args, **kwargs):
"""Execute a command on the host."""
if getattr(self, 'container', None):
if getattr(self, 'container', None) and getattr(self.container, 'name', None):
kwargs.setdefault('prefix', self.container.name)
proc = await Proc(*args, **kwargs)()
if kwargs.get('wait', True):
@ -33,8 +35,9 @@ class Script:
async def __call__(self, visitable, *args, **kwargs):
from .console_script import console_script
debug = console_script.parser.options.get('debug', False)
debug = console_script.options.get('debug', False)
self.args = args
for key, value in kwargs.items():
setattr(self, key, value)
@ -44,7 +47,14 @@ class Script:
async def clean():
for visitor in visitable.visitors:
if hasattr(visitor, 'clean_' + self.name):
result = getattr(visitor, 'clean_' + self.name)(self)
method = 'clean_' + self.name
result = getattr(visitor, method)(self)
if debug is True or 'visit' in str(debug):
print(
getattr(visitable, 'name', '') + ' | ',
'.'.join([type(visitor).__name__, method]),
' '.join(f'{k}={v}' for k, v in visitor.__dict__.items())
)
if result:
await result
@ -56,7 +66,7 @@ class Script:
if debug is True or 'visit' in str(debug):
print(
visitable.name + ' | ',
getattr(visitable, 'name', '') + ' | ',
'.'.join([type(visitor).__name__, method]),
' '.join(f'{k}={v}' for k, v in visitor.__dict__.items())
)
@ -69,20 +79,28 @@ class Script:
raise
async def run(self, *args, **kwargs):
pod = kwargs.get('pod')
for key, value in kwargs.items():
setattr(self, key, value)
if args:
containers = [c for c in pod.containers if c.name in args]
containers = [c for c in self.pod.containers if c.name in args]
else:
containers = pod.containers
containers = self.pod.containers
procs = []
for container in containers:
procs.append(self(
procs = [
copy.deepcopy(self)(
self.pod,
*args,
**kwargs,
)
]
procs += [
copy.deepcopy(self)(
container,
*args,
container=container,
**kwargs,
))
)
for container in containers
]
return await asyncio.gather(*procs)

50
podctl/scripts.py Normal file
View File

@ -0,0 +1,50 @@
import asyncio
import cli2
import copy
import os
import sys
from .build import Build
from .exceptions import WrongResult
from .proc import Proc
from .script import Script
class Name(Script):
color = cli2.GREEN
async def run(self, *args, **kwargs):
print(kwargs.get('pod').name)
class Down(Script):
color = cli2.RED
class Up(Script):
pass
class Run(Script):
async def run(self, *args, **kwargs):
for key, value in kwargs.items():
setattr(self, key, value)
try:
await self.exec(
'podman', 'pod', 'inspect', self.pod.name
)
print(f'{self.pod.name} | Pod ready')
except WrongResult:
print(f'{self.pod.name} | Pod creating')
await self.exec(
'podman', 'pod', 'create', '--name', self.pod.name,
)
print(f'{self.pod.name} | Pod created')
return await asyncio.gather(*[
copy.deepcopy(self)(
self.pod,
)
for container in self.pod.containers
])

View File

@ -1,5 +1,5 @@
import asyncio
from copy import copy
from copy import copy, deepcopy
class Visitable:
@ -7,13 +7,14 @@ class Visitable:
def __init__(self, *visitors, **scripts):
self.visitors = list(visitors)
self.scripts = scripts or {
k: v for k, v in self.default_scripts.items()
}
self.scripts = deepcopy(self.default_scripts)
self.scripts.update(scripts)
'''
def script(self, name):
script = copy(self.scripts[name])
return script
'''
def visitor(self, name):
for visitor in self.visitors:

View File

@ -13,3 +13,6 @@ class Base:
await script.umounts()
await script.umount()
proc = await script.exec('buildah', 'rm', script.ctr, raises=False)
def __repr__(self):
return f'Base({self.base})'

View File

@ -18,7 +18,7 @@ CI_VARS = (
class Commit:
def __init__(self, repo, tags=None, format=None, push=None, registry=None):
self.repo = repo
self.registry = registry
self.registry = registry or 'localhost'
self.push = push or os.getenv('CI')
# figure out registry host
@ -81,19 +81,5 @@ class Commit:
await script.exec('podman', 'push', f'{self.repo}:{tag}')
await script.umount()
async def run(self, script):
await script.exec(
'podman', 'run', '-d',
'--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])),
)
def __repr__(self):
return f'Commit({self.registry}/{self.repo}:{self.tags})'

View File

@ -10,6 +10,9 @@ class DumbInit:
def __init__(self, cmd):
self.cmd = cmd
def post_build(self, script):
async def post_build(self, script):
cmd = '--cmd "dumb-init bash -euxc \'%s\'"' % self.cmd
script.config(cmd)
await script.config(cmd)
def __repr__(self):
return f'DumbInit({self.cmd})'

View File

@ -150,3 +150,6 @@ class Packages:
cache_lists = os.path.join(cachedir, 'lists')
await script.mount(cache_lists, f'/var/lib/apt/lists')
return cachedir
def __repr__(self):
return f'Packages({self.packages})'