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): async def config(self, line):
"""Run buildah config.""" """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): async def copy(self, src, dst):
"""Run buildah copy to copy a file from host into container.""" """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): async def cexec(self, *args, user=None, **kwargs):
"""Execute a command in the container.""" """Execute a command in the container."""
@ -75,3 +75,6 @@ class Build(Script):
p = os.path.join(self.mnt, path[1:], c) p = os.path.join(self.mnt, path[1:], c)
if os.path.exists(p): if os.path.exists(p):
return p[len(str(self.mnt)):] 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 self.funckwargs['cmd'] = self.forward_args
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.options = dict()
def __call__(self, *args, **kwargs): def __call__(self, *args, **kwargs):
import inspect import inspect
from podctl.podfile import Podfile from podctl.podfile import Podfile
@ -43,18 +47,22 @@ class ConsoleScript(cli2.ConsoleScript):
self[name] = cli2.Callable( self[name] = cli2.Callable(
name, name,
cb, cb,
options=script.options, options={o.name: o for o in script.options},
color=getattr(script, 'color', cli2.YELLOW),
) )
return super().__call__(*args, **kwargs) return super().__call__(*args, **kwargs)
def call(self, command): def call(self, command):
self.options = self.parser.options
try: try:
return super().call(command) return super().call(command)
except Mistake as e: except Mistake as e:
print(e) print(e)
sys.exit(1) sys.exit(1)
except WrongResult: except WrongResult as e:
sys.exit(1) print(e)
sys.exit(e.proc.rc)
console_script = ConsoleScript(__doc__).add_module('podctl.console_script') 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 from .visitable import Visitable
class Container(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 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): 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 import os
from .build import Build from .build import Build
from .container import Container from .container import Container
from .exceptions import WrongResult from .scripts import *
from .script import Script
from .visitable import Visitable 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): class Pod(Visitable):
default_scripts = dict( default_scripts = dict(
build=Build(), build=Build(),
run=Script('run', 'Run a container command'),
up=Up('up', 'Start the stack'), 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 @property
def name(self): def name(self):
return os.getenv('POD', os.getcwd().split('/')[-1]) return os.getenv('POD', os.getcwd().split('/')[-1])
@ -53,7 +50,9 @@ class Pod(Visitable):
def script(self, name): def script(self, name):
async def cb(*args, **kwargs): async def cb(*args, **kwargs):
asyncio.events.get_event_loop() asyncio.events.get_event_loop()
script = copy.deepcopy(self.scripts[name])
kwargs['pod'] = self kwargs['pod'] = self
return await script.run(*args, **kwargs) return await self.scripts[name].run(*args, **kwargs)
return cb 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): def pipe_data_received(self, fd, data):
from .console_script import console_script 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): if (debug is True or 'out' in str(debug)) and fd in (1, 2):
for line in data.split(b'\n'): for line in data.split(b'\n'):
@ -34,6 +34,7 @@ class PrefixStreamProtocol(asyncio.subprocess.SubprocessStreamProtocol):
sys.stdout.flush() sys.stdout.flush()
super().pipe_data_received(fd, data) super().pipe_data_received(fd, data)
def protocol_factory(prefix): def protocol_factory(prefix):
def _p(): def _p():
return PrefixStreamProtocol( return PrefixStreamProtocol(
@ -77,8 +78,8 @@ class Proc:
raise Exception('Already called: ' + self.cmd) raise Exception('Already called: ' + self.cmd)
from .console_script import console_script 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 'proc' in str(debug): if debug is True or 'cmd' in str(debug):
if self.prefix: if self.prefix:
print(f'{self.prefix} | + {self.cmd}') print(f'{self.prefix} | + {self.cmd}')
else: else:
@ -109,7 +110,7 @@ class Proc:
if not self.communicated: if not self.communicated:
await self.communicate() await self.communicate()
if self.raises and self.proc.returncode: if self.raises and self.proc.returncode:
raise WrongResult() raise WrongResult(self)
return self return self
@property @property

View File

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

View File

@ -13,3 +13,6 @@ class Base:
await script.umounts() await script.umounts()
await script.umount() await script.umount()
proc = await script.exec('buildah', 'rm', script.ctr, raises=False) 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: class Commit:
def __init__(self, repo, tags=None, format=None, push=None, registry=None): def __init__(self, repo, tags=None, format=None, push=None, registry=None):
self.repo = repo self.repo = repo
self.registry = registry self.registry = registry or 'localhost'
self.push = push or os.getenv('CI') self.push = push or os.getenv('CI')
# figure out registry host # figure out registry host
@ -81,19 +81,5 @@ class Commit:
await script.exec('podman', 'push', f'{self.repo}:{tag}') await script.exec('podman', 'push', f'{self.repo}:{tag}')
await script.umount() await script.umount()
async def run(self, script): def __repr__(self):
await script.exec( return f'Commit({self.registry}/{self.repo}:{self.tags})'
'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])),
)

View File

@ -10,6 +10,9 @@ class DumbInit:
def __init__(self, cmd): def __init__(self, cmd):
self.cmd = cmd self.cmd = cmd
def post_build(self, script): async def post_build(self, script):
cmd = '--cmd "dumb-init bash -euxc \'%s\'"' % self.cmd 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') cache_lists = os.path.join(cachedir, 'lists')
await script.mount(cache_lists, f'/var/lib/apt/lists') await script.mount(cache_lists, f'/var/lib/apt/lists')
return cachedir return cachedir
def __repr__(self):
return f'Packages({self.packages})'