Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ea061db51f | ||
|
|
2db971234a | ||
|
|
41ec8db301 | ||
|
|
33c37f8e44 | ||
|
|
f5ab14d383 | ||
|
|
97255866f8 |
@@ -1,7 +0,0 @@
|
|||||||
from .actions import *
|
|
||||||
from .image import Image
|
|
||||||
from .strategies import *
|
|
||||||
from .output import Output
|
|
||||||
from .proc import Proc
|
|
||||||
from .targets import *
|
|
||||||
from .shlaxfile import Shlaxfile
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
from .copy import Copy
|
|
||||||
from .packages import Packages # noqa
|
|
||||||
from .base import Action # noqa
|
|
||||||
from .run import Run # noqa
|
|
||||||
from .pip import Pip
|
|
||||||
from .service import Service
|
|
||||||
|
|||||||
+87
-63
@@ -1,3 +1,4 @@
|
|||||||
|
from copy import deepcopy
|
||||||
import functools
|
import functools
|
||||||
import inspect
|
import inspect
|
||||||
import importlib
|
import importlib
|
||||||
@@ -5,9 +6,28 @@ import sys
|
|||||||
|
|
||||||
from ..output import Output
|
from ..output import Output
|
||||||
from ..exceptions import WrongResult
|
from ..exceptions import WrongResult
|
||||||
|
from ..result import Result
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class class_or_instance_method:
|
||||||
|
def __init__(self, f):
|
||||||
|
self.f = f
|
||||||
|
def __get__(self, instance, owner):
|
||||||
|
def newfunc(*args, **kwargs):
|
||||||
|
return self.f(
|
||||||
|
instance if instance is not None else owner,
|
||||||
|
*args,
|
||||||
|
**kwargs
|
||||||
|
)
|
||||||
|
return newfunc
|
||||||
|
|
||||||
|
|
||||||
class Action:
|
class Action:
|
||||||
|
display_variables = []
|
||||||
|
hide_variables = ['output']
|
||||||
|
default_steps = ['apply']
|
||||||
parent = None
|
parent = None
|
||||||
contextualize = []
|
contextualize = []
|
||||||
regexps = {
|
regexps = {
|
||||||
@@ -27,16 +47,10 @@ class Action:
|
|||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
self.args = args
|
self.args = args
|
||||||
self.kwargs = kwargs
|
for key, value in kwargs.items():
|
||||||
|
setattr(self, key, value)
|
||||||
@property
|
if isinstance(value, Action):
|
||||||
def context(self):
|
getattr(self, key).shlaxstep = True
|
||||||
if not self.parent:
|
|
||||||
if '_context' not in self.__dict__:
|
|
||||||
self._context = dict()
|
|
||||||
return self._context
|
|
||||||
else:
|
|
||||||
return self.parent.context
|
|
||||||
|
|
||||||
def actions_filter(self, results, f=None, **filters):
|
def actions_filter(self, results, f=None, **filters):
|
||||||
if f:
|
if f:
|
||||||
@@ -91,77 +105,72 @@ class Action:
|
|||||||
add(self)
|
add(self)
|
||||||
return self.actions_filter(children, f, **filters)
|
return self.actions_filter(children, f, **filters)
|
||||||
|
|
||||||
def __getattr__(self, name):
|
async def __call__(self, *targets, **options):
|
||||||
for a in self.parents() + self.sibblings() + self.children():
|
if not targets:
|
||||||
if name in a.contextualize:
|
from ..targets.localhost import Localhost
|
||||||
return getattr(a, name)
|
targets = [Localhost()]
|
||||||
raise AttributeError(f'{type(self).__name__} has no {name}')
|
|
||||||
|
|
||||||
async def call(self, *args, **kwargs):
|
output = Output(regexp=self.regexps, debug=True)
|
||||||
print(f'{self}.call(*args, **kwargs) not implemented')
|
results = []
|
||||||
sys.exit(1)
|
for target in targets:
|
||||||
|
target.output = output
|
||||||
def output_factory(self, *args, **kwargs):
|
if len(targets) > 1:
|
||||||
kwargs.setdefault('regexps', self.regexps)
|
output.prefix = target
|
||||||
return Output(**kwargs)
|
from copy import deepcopy
|
||||||
|
action = deepcopy(self)
|
||||||
async def __call__(self, *args, **kwargs):
|
action.target = target
|
||||||
self.call_args = args
|
result = Result(action, target)
|
||||||
self.call_kwargs = kwargs
|
results.append(result)
|
||||||
self.output = self.output_factory(*args, **kwargs)
|
action.result = result
|
||||||
self.output_start()
|
action.output = output
|
||||||
self.status = 'running'
|
for step in options.get('steps', None) or self.default_steps:
|
||||||
|
if step not in action.steps():
|
||||||
|
print(f'Failed to find {type(action).__name__}.{step}')
|
||||||
|
continue
|
||||||
|
action.step = step
|
||||||
|
output.start(action)
|
||||||
try:
|
try:
|
||||||
result = await self.call(*args, **kwargs)
|
await getattr(action, step)()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.output_fail(e)
|
output.fail(action, e)
|
||||||
self.status = 'fail'
|
action.result.status = 'fail'
|
||||||
proc = getattr(e, 'proc', None)
|
proc = getattr(e, 'proc', None)
|
||||||
if proc:
|
if proc:
|
||||||
result = proc.rc
|
result = proc.rc
|
||||||
else:
|
else:
|
||||||
raise
|
raise
|
||||||
else:
|
else:
|
||||||
self.output_success()
|
output.success(action)
|
||||||
if self.status == 'running':
|
result.status = 'success'
|
||||||
self.status = 'success'
|
|
||||||
finally:
|
finally:
|
||||||
clean = getattr(self, 'clean', None)
|
clean = getattr(action, 'clean', None)
|
||||||
if clean:
|
if clean:
|
||||||
self.output.clean(self)
|
output.clean(action)
|
||||||
await clean(*args, **kwargs)
|
await clean(target)
|
||||||
return result
|
|
||||||
|
|
||||||
def output_start(self):
|
return results
|
||||||
if self.kwargs.get('quiet', False):
|
|
||||||
return
|
|
||||||
self.output.start(self)
|
|
||||||
|
|
||||||
def output_fail(self, exception=None):
|
|
||||||
if self.kwargs.get('quiet', False):
|
|
||||||
return
|
|
||||||
self.output.fail(self, exception)
|
|
||||||
|
|
||||||
def output_success(self):
|
|
||||||
if self.kwargs.get('quiet', False):
|
|
||||||
return
|
|
||||||
self.output.success(self)
|
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return ' '.join([type(self).__name__] + list(self.args) + [
|
return ' '.join([type(self).__name__] + [
|
||||||
f'{k}={v}'
|
f'{k}={v}'
|
||||||
for k, v in self.kwargs.items()
|
for k, v in self.__dict__.items()
|
||||||
|
if (k in self.display_variables or not self.display_variables)
|
||||||
|
and (k not in self.hide_variables)
|
||||||
])
|
])
|
||||||
|
|
||||||
def colorized(self):
|
def colorized(self, colors):
|
||||||
return ' '.join([
|
return ' '.join([
|
||||||
self.output.colors['pink1']
|
colors['pink1']
|
||||||
+ type(self).__name__
|
+ type(self).__name__
|
||||||
+ self.output.colors['yellow']
|
+ '.'
|
||||||
] + list(self.args) + [
|
+ self.step
|
||||||
f'{self.output.colors["blue"]}{k}{self.output.colors["gray"]}={self.output.colors["green2"]}{v}'
|
+ colors['yellow']
|
||||||
for k, v in self.kwargs_output().items()
|
] + [
|
||||||
] + [self.output.colors['reset']])
|
f'{colors["blue"]}{k}{colors["gray"]}={colors["green2"]}{v}'
|
||||||
|
for k, v in self.__dict__.items()
|
||||||
|
if (k in self.display_variables or not self.display_variables)
|
||||||
|
and (k not in self.hide_variables)
|
||||||
|
] + [colors['reset']])
|
||||||
|
|
||||||
def callable(self):
|
def callable(self):
|
||||||
from ..targets import Localhost
|
from ..targets import Localhost
|
||||||
@@ -190,6 +199,7 @@ class Action:
|
|||||||
|
|
||||||
def action(self, action, *args, **kwargs):
|
def action(self, action, *args, **kwargs):
|
||||||
if isinstance(action, str):
|
if isinstance(action, str):
|
||||||
|
# import dotted module path string to action
|
||||||
import cli2
|
import cli2
|
||||||
a = cli2.Callable.factory(action).target
|
a = cli2.Callable.factory(action).target
|
||||||
if not a:
|
if not a:
|
||||||
@@ -200,12 +210,26 @@ class Action:
|
|||||||
action = a
|
action = a
|
||||||
|
|
||||||
p = action(*args, **kwargs)
|
p = action(*args, **kwargs)
|
||||||
|
p.parent = self
|
||||||
for parent in self.parents():
|
for parent in self.parents():
|
||||||
if hasattr(parent, 'actions'):
|
if hasattr(parent, 'actions'):
|
||||||
break
|
|
||||||
p.parent = parent
|
p.parent = parent
|
||||||
|
break
|
||||||
|
|
||||||
if 'actions' not in self.__dict__:
|
if 'actions' not in self.__dict__:
|
||||||
# "mutate" to Strategy
|
# "mutate" to Strategy
|
||||||
from ..strategies.script import Actions
|
from ..strategies.script import Actions
|
||||||
self.actions = Actions(self, [p])
|
self.actions = Actions(self, [p])
|
||||||
return p
|
return p
|
||||||
|
|
||||||
|
@class_or_instance_method
|
||||||
|
def steps(self):
|
||||||
|
return {
|
||||||
|
key: getattr(self, key)
|
||||||
|
for key in dir(self)
|
||||||
|
if key != 'steps' # avoid recursion
|
||||||
|
and (
|
||||||
|
key in self.default_steps
|
||||||
|
or getattr(getattr(self, key), 'shlaxstep', False)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|||||||
@@ -2,5 +2,6 @@ from .base import Action
|
|||||||
|
|
||||||
|
|
||||||
class Copy(Action):
|
class Copy(Action):
|
||||||
|
"""Copy files or directories to target."""
|
||||||
async def call(self, *args, **kwargs):
|
async def call(self, *args, **kwargs):
|
||||||
await self.copy(*self.args)
|
await self.copy(*self.args)
|
||||||
|
|||||||
@@ -0,0 +1,37 @@
|
|||||||
|
import hashlib
|
||||||
|
import secrets
|
||||||
|
import string
|
||||||
|
|
||||||
|
from .base import Action
|
||||||
|
|
||||||
|
|
||||||
|
class Htpasswd(Action):
|
||||||
|
"""Ensure a user is present in an htpasswd file."""
|
||||||
|
display_variables = ('user', 'path')
|
||||||
|
regexps = {
|
||||||
|
r'(.*)': '{red}\\1{gray}:${blue}\\2${blue}',
|
||||||
|
r'([^:]*):\\$([^$]*)\\$(.*)$': '{red}\\1{gray}:${blue}\\2${blue}\\3',
|
||||||
|
}
|
||||||
|
|
||||||
|
def __init__(self, user, path, **kwargs):
|
||||||
|
self.user = user
|
||||||
|
self.path = path
|
||||||
|
super().__init__(**kwargs)
|
||||||
|
|
||||||
|
async def apply(self):
|
||||||
|
found = False
|
||||||
|
htpasswd = await self.target.exec(
|
||||||
|
'cat', self.path, raises=False)
|
||||||
|
if htpasswd.rc == 0:
|
||||||
|
for line in htpasswd.out.split('\n'):
|
||||||
|
if line.startswith(self.user + ':'):
|
||||||
|
found = True
|
||||||
|
break
|
||||||
|
|
||||||
|
if not found:
|
||||||
|
self.password = ''.join(secrets.choice(
|
||||||
|
string.ascii_letters + string.digits
|
||||||
|
) for i in range(20))
|
||||||
|
hashed = hashlib.sha1(self.password.encode('utf8'))
|
||||||
|
line = f'{self.user}:\\$sha1\\${hashed.hexdigest()}'
|
||||||
|
await self.target.exec(f'echo {line} >> {self.path}')
|
||||||
@@ -12,14 +12,13 @@ from .base import Action
|
|||||||
|
|
||||||
class Packages(Action):
|
class Packages(Action):
|
||||||
"""
|
"""
|
||||||
The Packages visitor wraps around the container's package manager.
|
Package manager abstract layer with caching.
|
||||||
|
|
||||||
It's a central piece of the build process, and does iterate over other
|
It's a central piece of the build process, and does iterate over other
|
||||||
container visitors in order to pick up packages. For example, the Pip
|
container visitors in order to pick up packages. For example, the Pip
|
||||||
visitor will declare ``self.packages = dict(apt=['python3-pip'])``, and the
|
visitor will declare ``self.packages = dict(apt=['python3-pip'])``, and the
|
||||||
Packages visitor will pick it up.
|
Packages visitor will pick it up.
|
||||||
"""
|
"""
|
||||||
contextualize = ['mgr']
|
|
||||||
regexps = {
|
regexps = {
|
||||||
#r'Installing ([\w\d-]+)': '{cyan}\\1',
|
#r'Installing ([\w\d-]+)': '{cyan}\\1',
|
||||||
r'Installing': '{cyan}lol',
|
r'Installing': '{cyan}lol',
|
||||||
@@ -106,12 +105,12 @@ class Packages(Action):
|
|||||||
print(f'{self.container.name} | Waiting for update ...')
|
print(f'{self.container.name} | Waiting for update ...')
|
||||||
await asyncio.sleep(1)
|
await asyncio.sleep(1)
|
||||||
|
|
||||||
async def call(self, *args, **kwargs):
|
async def apply(self):
|
||||||
cached = getattr(self, '_pagkages_mgr', None)
|
cached = getattr(self.target, 'pkgmgr', None)
|
||||||
if cached:
|
if cached:
|
||||||
self.mgr = cached
|
self.mgr = cached
|
||||||
else:
|
else:
|
||||||
mgr = await self.which(*self.mgrs.keys())
|
mgr = await self.target.which(*self.mgrs.keys())
|
||||||
if mgr:
|
if mgr:
|
||||||
self.mgr = mgr[0].split('/')[-1]
|
self.mgr = mgr[0].split('/')[-1]
|
||||||
|
|
||||||
@@ -122,7 +121,7 @@ class Packages(Action):
|
|||||||
if not getattr(self, '_packages_upgraded', None):
|
if not getattr(self, '_packages_upgraded', None):
|
||||||
await self.update()
|
await self.update()
|
||||||
if self.kwargs.get('upgrade', True):
|
if self.kwargs.get('upgrade', True):
|
||||||
await self.rexec(self.cmds['upgrade'])
|
await self.target.exec(self.cmds['upgrade'], user='root')
|
||||||
self._packages_upgraded = True
|
self._packages_upgraded = True
|
||||||
|
|
||||||
packages = []
|
packages = []
|
||||||
@@ -136,7 +135,7 @@ class Packages(Action):
|
|||||||
else:
|
else:
|
||||||
packages.append(package)
|
packages.append(package)
|
||||||
|
|
||||||
await self.rexec(*self.cmds['install'].split(' ') + packages)
|
await self.target.exec(*self.cmds['install'].split(' ') + packages, user='root')
|
||||||
|
|
||||||
async def apk_setup(self):
|
async def apk_setup(self):
|
||||||
cachedir = os.path.join(self.cache_root, self.mgr)
|
cachedir = os.path.join(self.cache_root, self.mgr)
|
||||||
|
|||||||
@@ -5,6 +5,8 @@ from .base import Action
|
|||||||
|
|
||||||
|
|
||||||
class Pip(Action):
|
class Pip(Action):
|
||||||
|
"""Pip abstraction layer."""
|
||||||
|
|
||||||
def __init__(self, *pip_packages, pip=None, requirements=None):
|
def __init__(self, *pip_packages, pip=None, requirements=None):
|
||||||
self.requirements = requirements
|
self.requirements = requirements
|
||||||
super().__init__(*pip_packages, pip=pip, requirements=requirements)
|
super().__init__(*pip_packages, pip=pip, requirements=requirements)
|
||||||
|
|||||||
@@ -1,14 +1,14 @@
|
|||||||
from ..targets.buildah import Buildah
|
|
||||||
from ..targets.docker import Docker
|
|
||||||
|
|
||||||
from .base import Action
|
from .base import Action
|
||||||
|
|
||||||
|
|
||||||
class Run(Action):
|
class Run(Action):
|
||||||
|
"""Run a script or command on a target."""
|
||||||
async def call(self, *args, **kwargs):
|
async def call(self, *args, **kwargs):
|
||||||
image = self.kwargs.get('image', None)
|
image = self.kwargs.get('image', None)
|
||||||
if not image:
|
if not image:
|
||||||
return await self.exec(*self.args, **self.kwargs)
|
return await self.exec(*self.args, **self.kwargs)
|
||||||
|
from ..targets.buildah import Buildah
|
||||||
|
from ..targets.docker import Docker
|
||||||
if isinstance(image, Buildah):
|
if isinstance(image, Buildah):
|
||||||
breakpoint()
|
breakpoint()
|
||||||
result = await self.action(image, *args, **kwargs)
|
result = await self.action(image, *args, **kwargs)
|
||||||
|
|||||||
@@ -4,6 +4,9 @@ from .base import Action
|
|||||||
|
|
||||||
|
|
||||||
class Service(Action):
|
class Service(Action):
|
||||||
|
"""
|
||||||
|
Manage a systemd service.
|
||||||
|
"""
|
||||||
def __init__(self, *names, state=None):
|
def __init__(self, *names, state=None):
|
||||||
self.state = state or 'started'
|
self.state = state or 'started'
|
||||||
self.names = names
|
self.names = names
|
||||||
|
|||||||
+148
-124
@@ -1,140 +1,165 @@
|
|||||||
'''
|
"""
|
||||||
shlax is a micro-framework to orchestrate commands.
|
Shlax automation tool manual
|
||||||
|
|
||||||
shlax yourfile.py: to list actions you have declared.
|
Shlax is built mostly around 3 moving pieces:
|
||||||
shlax yourfile.py <action>: to execute a given action
|
- Target: a target host and protocol
|
||||||
#!/usr/bin/env shlax: when making yourfile.py an executable.
|
- Action: execute a shlax action
|
||||||
'''
|
- Strategy: defines how to apply actions on targets (scripted only)
|
||||||
|
|
||||||
|
Shlax executes mostly in 3 ways:
|
||||||
|
- Execute actions on targets with the command line
|
||||||
|
- With your shlaxfile as first argument: offer defined Actions
|
||||||
|
- With the name of a module in shlax.repo: a community maintained shlaxfile
|
||||||
|
"""
|
||||||
|
|
||||||
import asyncio
|
|
||||||
import cli2
|
|
||||||
import copy
|
import copy
|
||||||
|
import cli2
|
||||||
import inspect
|
import inspect
|
||||||
|
import importlib
|
||||||
|
import glob
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
from .exceptions import *
|
from .actions.base import Action
|
||||||
from .shlaxfile import Shlaxfile
|
from .exceptions import ShlaxException, WrongResult
|
||||||
from .targets import Localhost
|
from .strategies import Script
|
||||||
|
|
||||||
|
|
||||||
async def runall(*args, **kwargs):
|
|
||||||
for name, action in cli.shlaxfile.actions.items():
|
|
||||||
await Localhost(action)(*args, **kwargs)
|
|
||||||
|
|
||||||
|
|
||||||
@cli2.option('debug', alias='d', help='Display debug output.')
|
|
||||||
async def test(*args, **kwargs):
|
|
||||||
"""Run podctl test over a bunch of paths."""
|
|
||||||
report = []
|
|
||||||
|
|
||||||
for arg in args:
|
|
||||||
candidates = [
|
|
||||||
os.path.join(os.getcwd(), arg, 'pod.py'),
|
|
||||||
os.path.join(os.getcwd(), arg, 'pod_test.py'),
|
|
||||||
]
|
|
||||||
for candidate in candidates:
|
|
||||||
if not os.path.exists(candidate):
|
|
||||||
continue
|
|
||||||
podfile = Podfile.factory(candidate)
|
|
||||||
|
|
||||||
# disable push
|
|
||||||
for name, container in podfile.containers.items():
|
|
||||||
commit = container.visitor('commit')
|
|
||||||
if commit:
|
|
||||||
commit.push = False
|
|
||||||
|
|
||||||
output.print(
|
|
||||||
'\n\x1b[1;38;5;160;48;5;118m BUILD START \x1b[0m'
|
|
||||||
+ ' ' + podfile.path + '\n'
|
|
||||||
)
|
|
||||||
|
|
||||||
old_exit_code = console_script.exit_code
|
|
||||||
console_script.exit_code = 0
|
|
||||||
try:
|
|
||||||
await podfile.pod.script('build')()
|
|
||||||
except Exception as e:
|
|
||||||
report.append(('build ' + candidate, False))
|
|
||||||
continue
|
|
||||||
|
|
||||||
if console_script.exit_code != 0:
|
|
||||||
report.append(('build ' + candidate, False))
|
|
||||||
continue
|
|
||||||
console_script.exit_code = old_exit_code
|
|
||||||
|
|
||||||
for name, test in podfile.tests.items():
|
|
||||||
name = '::'.join([podfile.path, name])
|
|
||||||
output.print(
|
|
||||||
'\n\x1b[1;38;5;160;48;5;118m TEST START \x1b[0m'
|
|
||||||
+ ' ' + name + '\n'
|
|
||||||
)
|
|
||||||
|
|
||||||
try:
|
|
||||||
await test(podfile.pod)
|
|
||||||
except Exception as e:
|
|
||||||
report.append((name, False))
|
|
||||||
output.print('\x1b[1;38;5;15;48;5;196m TEST FAIL \x1b[0m' + name)
|
|
||||||
else:
|
|
||||||
report.append((name, True))
|
|
||||||
output.print('\x1b[1;38;5;200;48;5;44m TEST SUCCESS \x1b[0m' + name)
|
|
||||||
output.print('\n')
|
|
||||||
|
|
||||||
print('\n')
|
|
||||||
|
|
||||||
for name, success in report:
|
|
||||||
if success:
|
|
||||||
output.print('\n\x1b[1;38;5;200;48;5;44m TEST SUCCESS \x1b[0m' + name)
|
|
||||||
else:
|
|
||||||
output.print('\n\x1b[1;38;5;15;48;5;196m TEST FAIL \x1b[0m' + name)
|
|
||||||
|
|
||||||
print('\n')
|
|
||||||
|
|
||||||
success = [*filter(lambda i: i[1], report)]
|
|
||||||
failures = [*filter(lambda i: not i[1], report)]
|
|
||||||
|
|
||||||
output.print(
|
|
||||||
'\n\x1b[1;38;5;200;48;5;44m TEST TOTAL: \x1b[0m'
|
|
||||||
+ str(len(report))
|
|
||||||
)
|
|
||||||
if success:
|
|
||||||
output.print(
|
|
||||||
'\n\x1b[1;38;5;200;48;5;44m TEST SUCCESS: \x1b[0m'
|
|
||||||
+ str(len(success))
|
|
||||||
)
|
|
||||||
if failures:
|
|
||||||
output.print(
|
|
||||||
'\n\x1b[1;38;5;15;48;5;196m TEST FAIL: \x1b[0m'
|
|
||||||
+ str(len(failures))
|
|
||||||
)
|
|
||||||
|
|
||||||
if failures:
|
|
||||||
console_script.exit_code = 1
|
|
||||||
|
|
||||||
|
|
||||||
class ConsoleScript(cli2.ConsoleScript):
|
class ConsoleScript(cli2.ConsoleScript):
|
||||||
def __call__(self, *args, **kwargs):
|
class Parser(cli2.Parser):
|
||||||
self.shlaxfile = None
|
def __init__(self, *args, **kwargs):
|
||||||
shlaxfile = sys.argv.pop(1) if len(sys.argv) > 1 else ''
|
self.targets = dict()
|
||||||
if os.path.exists(shlaxfile.split('::')[0]):
|
super().__init__(*args, **kwargs)
|
||||||
self.shlaxfile = Shlaxfile()
|
|
||||||
self.shlaxfile.parse(shlaxfile)
|
def append(self, arg):
|
||||||
for name, action in self.shlaxfile.actions.items():
|
if '=' not in arg and '@' in arg:
|
||||||
self[name] = cli2.Callable(
|
if '://' in arg:
|
||||||
name,
|
kind, spec = arg.split('://')
|
||||||
action.callable(),
|
else:
|
||||||
options={
|
kind = 'ssh'
|
||||||
k: cli2.Option(name=k, **v)
|
spec = arg
|
||||||
for k, v in action.options.items()
|
|
||||||
},
|
mod = importlib.import_module('shlax.targets.' + kind)
|
||||||
color=getattr(action, 'color', cli2.YELLOW),
|
target = getattr(mod, kind.capitalize())(spec)
|
||||||
|
self.targets[str(target)] = target
|
||||||
|
else:
|
||||||
|
super().append(arg)
|
||||||
|
|
||||||
|
def __call__(self):
|
||||||
|
if len(sys.argv) > 1 and os.path.exists(sys.argv[1]):
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
scripts = glob.glob(os.path.join(
|
||||||
|
os.path.dirname(__file__), 'actions', '*.py'))
|
||||||
|
for script in scripts:
|
||||||
|
modname = script.split('/')[-1].replace('.py', '')
|
||||||
|
mod = importlib.import_module('shlax.actions.' + modname)
|
||||||
|
for key, value in mod.__dict__.items():
|
||||||
|
if key.lower() != modname:
|
||||||
|
continue
|
||||||
|
break
|
||||||
|
self[modname] = cli2.Callable(
|
||||||
|
modname, self.action_class(value))
|
||||||
|
|
||||||
|
scripts = glob.glob(os.path.join(
|
||||||
|
os.path.dirname(__file__), 'repo', '*.py'))
|
||||||
|
for script in scripts:
|
||||||
|
modname = script.split('/')[-1].replace('.py', '')
|
||||||
|
mod = importlib.import_module('shlax.repo.' + modname)
|
||||||
|
self[modname] = cli2.Group(key, doc=inspect.getdoc(mod))
|
||||||
|
for key, value in mod.__dict__.items():
|
||||||
|
if not isinstance(value, Action):
|
||||||
|
continue
|
||||||
|
doc = (inspect.getdoc(mod) or '').split("\n")[0]
|
||||||
|
if key == 'main':
|
||||||
|
if len(value.steps()) == 1:
|
||||||
|
self[modname] = cli2.Callable(
|
||||||
|
modname, self.action(value), doc=doc)
|
||||||
|
else:
|
||||||
|
for name, method in value.steps().items():
|
||||||
|
self[modname][name] = cli2.Callable(
|
||||||
|
modname, self.action(value),
|
||||||
|
doc=inspect.getdoc(method)
|
||||||
)
|
)
|
||||||
return super().__call__(*args, **kwargs)
|
else:
|
||||||
|
if len(value.steps()) == 1:
|
||||||
|
self[modname][key] = cli2.Callable(
|
||||||
|
modname, self.action(value), doc=doc)
|
||||||
|
else:
|
||||||
|
self[modname][key] = cli2.Group('steps')
|
||||||
|
for step in value.steps():
|
||||||
|
self[modname][key][step] = cli2.Callable(
|
||||||
|
modname, self.action(value), doc='lol')
|
||||||
|
|
||||||
|
return super().__call__()
|
||||||
|
|
||||||
|
def action(self, action):
|
||||||
|
async def cb(*args, **kwargs):
|
||||||
|
options = dict(steps=args)
|
||||||
|
options.update(self.parser.options)
|
||||||
|
# UnboundLocalError: local variable 'action' referenced before assignment
|
||||||
|
# ??? gotta be missing something, commenting meanwhile
|
||||||
|
# action = copy.deepcopy(action)
|
||||||
|
return await action(*self.parser.targets, **options)
|
||||||
|
return cb
|
||||||
|
|
||||||
|
def action_class(self, action_class):
|
||||||
|
async def cb(*args, **kwargs):
|
||||||
|
argspec = inspect.getfullargspec(action_class)
|
||||||
|
required = argspec.args[1:]
|
||||||
|
missing = []
|
||||||
|
for i, name in enumerate(required):
|
||||||
|
if len(args) - 1 <= i:
|
||||||
|
continue
|
||||||
|
if name in kwargs:
|
||||||
|
continue
|
||||||
|
missing.append(name)
|
||||||
|
if missing:
|
||||||
|
if not args:
|
||||||
|
print('No args provided after action name ' + action_class.__name__.lower())
|
||||||
|
print('Required arguments: ' + ', '.join(argspec.args[1:]))
|
||||||
|
if args:
|
||||||
|
print('Provided: ' + ', '.join(args))
|
||||||
|
print('Missing arguments: ' + ', '.join(missing))
|
||||||
|
print('Try to just add args on the command line separated with a space')
|
||||||
|
print(inspect.getdoc(action_class))
|
||||||
|
example = 'Example: shlax action '
|
||||||
|
example += action_class.__name__.lower()
|
||||||
|
if args:
|
||||||
|
example += ' ' + ' '.join(args)
|
||||||
|
example += ' ' + ' '.join(missing)
|
||||||
|
print(example)
|
||||||
|
return
|
||||||
|
|
||||||
|
_args = []
|
||||||
|
steps = []
|
||||||
|
for arg in args:
|
||||||
|
if arg in action_class.steps():
|
||||||
|
steps.append(arg)
|
||||||
|
else:
|
||||||
|
_args.append(arg)
|
||||||
|
|
||||||
|
options = dict(steps=steps)
|
||||||
|
|
||||||
|
'''
|
||||||
|
varargs = argspec.varargs
|
||||||
|
if varargs:
|
||||||
|
extra = args[len(argspec.args) - 1:]
|
||||||
|
args = args[:len(argspec.args) - 1]
|
||||||
|
options = dict(steps=extra)
|
||||||
|
else:
|
||||||
|
extra = args[len(argspec.args) - 1:]
|
||||||
|
args = args[:len(argspec.args) - 1]
|
||||||
|
options = dict(steps=extra)
|
||||||
|
'''
|
||||||
|
options.update(self.parser.options)
|
||||||
|
return await action_class(*_args, **kwargs)(*self.parser.targets, **options)
|
||||||
|
cb.__doc__ = (inspect.getdoc(action_class) or '').split("\n")[0]
|
||||||
|
return cb
|
||||||
|
|
||||||
def call(self, command):
|
def call(self, command):
|
||||||
kwargs = copy.copy(self.parser.funckwargs)
|
|
||||||
kwargs.update(self.parser.options)
|
|
||||||
try:
|
try:
|
||||||
return command(*self.parser.funcargs, **kwargs)
|
return super().call(command)
|
||||||
except WrongResult as e:
|
except WrongResult as e:
|
||||||
print(e)
|
print(e)
|
||||||
self.exit_code = e.proc.rc
|
self.exit_code = e.proc.rc
|
||||||
@@ -142,5 +167,4 @@ class ConsoleScript(cli2.ConsoleScript):
|
|||||||
print(e)
|
print(e)
|
||||||
self.exit_code = 1
|
self.exit_code = 1
|
||||||
|
|
||||||
|
|
||||||
cli = ConsoleScript(__doc__).add_module('shlax.cli')
|
cli = ConsoleScript(__doc__).add_module('shlax.cli')
|
||||||
|
|||||||
@@ -1,37 +0,0 @@
|
|||||||
from copy import deepcopy
|
|
||||||
import yaml
|
|
||||||
|
|
||||||
from shlax import *
|
|
||||||
|
|
||||||
|
|
||||||
class GitLabCI(Script):
|
|
||||||
async def call(self, *args, write=True, **kwargs):
|
|
||||||
output = dict()
|
|
||||||
for key, value in self.kwargs.items():
|
|
||||||
if isinstance(value, dict):
|
|
||||||
output[key] = deepcopy(value)
|
|
||||||
image = output[key].get('image', 'alpine')
|
|
||||||
if hasattr(image, 'image'):
|
|
||||||
output[key]['image'] = image.image.repository + ':$CI_COMMIT_SHORT_SHA'
|
|
||||||
else:
|
|
||||||
output[key] = value
|
|
||||||
|
|
||||||
output = yaml.dump(output)
|
|
||||||
if kwargs['debug'] is True:
|
|
||||||
self.output(output)
|
|
||||||
if write:
|
|
||||||
with open('.gitlab-ci.yml', 'w+') as f:
|
|
||||||
f.write(output)
|
|
||||||
|
|
||||||
from shlax.cli import cli
|
|
||||||
for arg in args:
|
|
||||||
job = self.kwargs[arg]
|
|
||||||
_args = []
|
|
||||||
if not isinstance(job['image'], str):
|
|
||||||
image = str(job['image'].image)
|
|
||||||
else:
|
|
||||||
image = job['image']
|
|
||||||
await self.action('Docker', Run(job['script']), image=image)(*_args, **kwargs)
|
|
||||||
|
|
||||||
def colorized(self):
|
|
||||||
return type(self).__name__
|
|
||||||
+5
-5
@@ -104,7 +104,7 @@ class Output:
|
|||||||
self.colors['purplebold'],
|
self.colors['purplebold'],
|
||||||
'! TEST ',
|
'! TEST ',
|
||||||
self.colors['reset'],
|
self.colors['reset'],
|
||||||
action.colorized(),
|
action.colorized(self.colors),
|
||||||
'\n',
|
'\n',
|
||||||
]))
|
]))
|
||||||
|
|
||||||
@@ -114,7 +114,7 @@ class Output:
|
|||||||
self.colors['bluebold'],
|
self.colors['bluebold'],
|
||||||
'+ CLEAN ',
|
'+ CLEAN ',
|
||||||
self.colors['reset'],
|
self.colors['reset'],
|
||||||
action.colorized(),
|
action.colorized(self.colors),
|
||||||
'\n',
|
'\n',
|
||||||
]))
|
]))
|
||||||
|
|
||||||
@@ -124,7 +124,7 @@ class Output:
|
|||||||
self.colors['orangebold'],
|
self.colors['orangebold'],
|
||||||
'⚠ START ',
|
'⚠ START ',
|
||||||
self.colors['reset'],
|
self.colors['reset'],
|
||||||
action.colorized(),
|
action.colorized(self.colors),
|
||||||
'\n',
|
'\n',
|
||||||
]))
|
]))
|
||||||
|
|
||||||
@@ -134,7 +134,7 @@ class Output:
|
|||||||
self.colors['greenbold'],
|
self.colors['greenbold'],
|
||||||
'✔ SUCCESS ',
|
'✔ SUCCESS ',
|
||||||
self.colors['reset'],
|
self.colors['reset'],
|
||||||
action.colorized() if hasattr(action, 'colorized') else str(action),
|
action.colorized(self.colors) if hasattr(action, 'colorized') else str(action),
|
||||||
'\n',
|
'\n',
|
||||||
]))
|
]))
|
||||||
|
|
||||||
@@ -144,6 +144,6 @@ class Output:
|
|||||||
self.colors['redbold'],
|
self.colors['redbold'],
|
||||||
'✘ FAIL ',
|
'✘ FAIL ',
|
||||||
self.colors['reset'],
|
self.colors['reset'],
|
||||||
action.colorized() if hasattr(action, 'colorized') else str(action),
|
action.colorized(self.colors) if hasattr(action, 'colorized') else str(action),
|
||||||
'\n',
|
'\n',
|
||||||
]))
|
]))
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
|
||||||
|
from .targets import Localhost
|
||||||
|
|
||||||
|
|
||||||
|
class Play:
|
||||||
|
def __init__(self, *actions, targets=None, options=None):
|
||||||
|
self.options = options or {}
|
||||||
|
self.targets = targets or dict(localhost=Localhost())
|
||||||
|
self.actions =
|
||||||
Executable
+35
@@ -0,0 +1,35 @@
|
|||||||
|
#!/usr/bin/env shlax
|
||||||
|
"""
|
||||||
|
Manage a traefik container maintained by Shlax community.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from shlax.shortcuts import *
|
||||||
|
|
||||||
|
|
||||||
|
main = Docker(
|
||||||
|
name='traefik',
|
||||||
|
image='traefik:v2.0.0',
|
||||||
|
install=Htpasswd(
|
||||||
|
'./htpasswd', 'root', doc='Install root user in ./htpasswd'
|
||||||
|
),
|
||||||
|
networks=['web'],
|
||||||
|
command=[
|
||||||
|
'--entrypoints.web.address=:80',
|
||||||
|
'--providers.docker',
|
||||||
|
'--api',
|
||||||
|
],
|
||||||
|
ports=[
|
||||||
|
'80:80',
|
||||||
|
'443:443',
|
||||||
|
],
|
||||||
|
volumes=[
|
||||||
|
'/var/run/docker.sock:/var/run/docker.sock:ro',
|
||||||
|
'/etc/traefik/acme/:/etc/traefik/acme/',
|
||||||
|
'/etc/traefik/htpasswd:/htpasswd:ro',
|
||||||
|
],
|
||||||
|
labels=[
|
||||||
|
'traefik.http.routers.traefik.rule=Host(`{{ url.split("/")[2] }}`)',
|
||||||
|
'traefik.http.routers.traefik.service=api@internal',
|
||||||
|
'traefik.http.routers.traefik.entrypoints=web',
|
||||||
|
],
|
||||||
|
)
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
|
||||||
|
|
||||||
|
class Result:
|
||||||
|
def __init__(self, action, target):
|
||||||
|
self.action = action
|
||||||
|
self.target = target
|
||||||
|
self.status = 'pending'
|
||||||
+2
-1
@@ -16,10 +16,11 @@ class Shlaxfile:
|
|||||||
spec.loader.exec_module(mod)
|
spec.loader.exec_module(mod)
|
||||||
for name, value in mod.__dict__.items():
|
for name, value in mod.__dict__.items():
|
||||||
if isinstance(value, Action):
|
if isinstance(value, Action):
|
||||||
value.name = name
|
value.__name__ = name
|
||||||
self.actions[name] = value
|
self.actions[name] = value
|
||||||
elif callable(value) and getattr(value, '__name__', '').startswith('test_'):
|
elif callable(value) and getattr(value, '__name__', '').startswith('test_'):
|
||||||
self.tests[value.__name__] = value
|
self.tests[value.__name__] = value
|
||||||
|
|
||||||
self.paths.append(path)
|
self.paths.append(path)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
|||||||
@@ -0,0 +1,14 @@
|
|||||||
|
from .actions.copy import Copy
|
||||||
|
from .actions.packages import Packages # noqa
|
||||||
|
from .actions.base import Action # noqa
|
||||||
|
from .actions.htpasswd import Htpasswd
|
||||||
|
from .actions.run import Run # noqa
|
||||||
|
from .actions.pip import Pip
|
||||||
|
from .actions.service import Service
|
||||||
|
|
||||||
|
from .targets.buildah import Buildah
|
||||||
|
from .targets.docker import Docker
|
||||||
|
from .targets.localhost import Localhost
|
||||||
|
from .targets.ssh import Ssh
|
||||||
|
|
||||||
|
|
||||||
+20
-3
@@ -1,14 +1,31 @@
|
|||||||
import os
|
import os
|
||||||
from .script import Script
|
from .script import Script
|
||||||
|
from ..image import Image
|
||||||
|
|
||||||
|
|
||||||
class Container(Script):
|
class Container(Script):
|
||||||
|
"""
|
||||||
|
Wolcome to crazy container control cli
|
||||||
|
|
||||||
|
Such wow
|
||||||
|
"""
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
kwargs.setdefault('start', dict())
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
async def call(self, *args, **kwargs):
|
async def call(self, *args, **kwargs):
|
||||||
if not args or 'build' in args:
|
if step('build'):
|
||||||
await self.kwargs['build'](**kwargs)
|
await self.kwargs['build'](**kwargs)
|
||||||
self.image = self.kwargs['build'].image
|
self.image = self.kwargs['build'].image
|
||||||
|
else:
|
||||||
|
self.image = kwargs.get('image', 'alpine')
|
||||||
|
if isinstance(self.image, str):
|
||||||
|
self.image = Image(self.image)
|
||||||
|
|
||||||
if not args or 'test' in args:
|
if step('install'):
|
||||||
|
await self.install(*args, **kwargs)
|
||||||
|
|
||||||
|
if step('test'):
|
||||||
self.output.test(self)
|
self.output.test(self)
|
||||||
await self.action('Docker',
|
await self.action('Docker',
|
||||||
*self.kwargs['test'].actions,
|
*self.kwargs['test'].actions,
|
||||||
@@ -17,7 +34,7 @@ class Container(Script):
|
|||||||
workdir='/app',
|
workdir='/app',
|
||||||
)(**kwargs)
|
)(**kwargs)
|
||||||
|
|
||||||
if not args or 'push' in args:
|
if step('push'):
|
||||||
await self.image.push(action=self)
|
await self.image.push(action=self)
|
||||||
|
|
||||||
#name = kwargs.get('name', os.getcwd()).split('/')[-1]
|
#name = kwargs.get('name', os.getcwd()).split('/')[-1]
|
||||||
|
|||||||
@@ -14,16 +14,17 @@ class Actions(list):
|
|||||||
self.append(action)
|
self.append(action)
|
||||||
|
|
||||||
def append(self, value):
|
def append(self, value):
|
||||||
value = copy.deepcopy(value)
|
action = copy.deepcopy(value)
|
||||||
value.parent = self.owner
|
action.parent = self.owner
|
||||||
value.status = 'pending'
|
action.status = 'pending'
|
||||||
super().append(value)
|
super().append(action)
|
||||||
|
|
||||||
|
|
||||||
class Script(Action):
|
class Script(Action):
|
||||||
contextualize = ['shargs', 'exec', 'rexec', 'env', 'which', 'copy']
|
contextualize = ['shargs', 'exec', 'rexec', 'env', 'which', 'copy']
|
||||||
|
|
||||||
def __init__(self, *actions, **kwargs):
|
def __init__(self, *actions, **kwargs):
|
||||||
|
self.home = kwargs.pop('home', os.getcwd())
|
||||||
super().__init__(**kwargs)
|
super().__init__(**kwargs)
|
||||||
self.actions = Actions(self, actions)
|
self.actions = Actions(self, actions)
|
||||||
|
|
||||||
@@ -32,3 +33,9 @@ class Script(Action):
|
|||||||
result = await action(*args, **kwargs)
|
result = await action(*args, **kwargs)
|
||||||
if action.status != 'success':
|
if action.status != 'success':
|
||||||
break
|
break
|
||||||
|
|
||||||
|
def pollute(self, gbls):
|
||||||
|
for name, script in self.kwargs.items():
|
||||||
|
if not isinstance(script, Script):
|
||||||
|
continue
|
||||||
|
gbls[name] = script
|
||||||
|
|||||||
@@ -0,0 +1,7 @@
|
|||||||
|
from .script import Script
|
||||||
|
|
||||||
|
class Test(Script):
|
||||||
|
async def call(self, *args, backend=None, **kwargs):
|
||||||
|
backend = backend or 'Docker'
|
||||||
|
breakpoint()
|
||||||
|
return await self.action(backend, self.actions, **kwargs)
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
from .buildah import Buildah
|
|
||||||
from .docker import Docker
|
|
||||||
from .localhost import Localhost
|
|
||||||
from .ssh import Ssh
|
|
||||||
|
|||||||
+53
-18
@@ -7,14 +7,17 @@ from .localhost import Localhost
|
|||||||
|
|
||||||
|
|
||||||
class Docker(Localhost):
|
class Docker(Localhost):
|
||||||
contextualize = Localhost.contextualize + ['mnt', 'ctr', 'mount']
|
"""Manage a docker container."""
|
||||||
|
default_steps = ['install', 'up']
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
self.image = kwargs.get('image', 'alpine')
|
self.image = kwargs.get('image', 'alpine')
|
||||||
|
self.name = kwargs.get('name', os.getcwd().split('/')[-1])
|
||||||
|
|
||||||
if not isinstance(self.image, Image):
|
if not isinstance(self.image, Image):
|
||||||
self.image = Image(self.image)
|
self.image = Image(self.image)
|
||||||
|
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
self.context['ctr'] = None
|
|
||||||
|
|
||||||
def shargs(self, *args, daemon=False, **kwargs):
|
def shargs(self, *args, daemon=False, **kwargs):
|
||||||
if args[0] == 'docker':
|
if args[0] == 'docker':
|
||||||
@@ -26,9 +29,9 @@ class Docker(Localhost):
|
|||||||
|
|
||||||
args, kwargs = super().shargs(*args, **kwargs)
|
args, kwargs = super().shargs(*args, **kwargs)
|
||||||
|
|
||||||
if self.context['ctr']:
|
if self.name:
|
||||||
executor = 'exec'
|
executor = 'exec'
|
||||||
extra = [self.context['ctr']]
|
extra = [self.name]
|
||||||
return [self.kwargs.get('docker', 'docker'), executor, '-t'] + extra + list(args), kwargs
|
return [self.kwargs.get('docker', 'docker'), executor, '-t'] + extra + list(args), kwargs
|
||||||
|
|
||||||
executor = 'run'
|
executor = 'run'
|
||||||
@@ -39,22 +42,39 @@ class Docker(Localhost):
|
|||||||
return [self.kwargs.get('docker', 'docker'), executor, '-t'] + extra + [str(self.image)] + list(args), kwargs
|
return [self.kwargs.get('docker', 'docker'), executor, '-t'] + extra + [str(self.image)] + list(args), kwargs
|
||||||
|
|
||||||
async def call(self, *args, **kwargs):
|
async def call(self, *args, **kwargs):
|
||||||
name = kwargs.get('name', os.getcwd()).split('/')[-1]
|
def step(step):
|
||||||
self.context['ctr'] = (
|
return not args or step in args
|
||||||
await self.exec(
|
|
||||||
|
# self.name = (
|
||||||
|
# await self.exec(
|
||||||
|
# 'docker', 'ps', '-aq', '--filter',
|
||||||
|
# 'name=' + self.name,
|
||||||
|
# raises=False
|
||||||
|
# )
|
||||||
|
# ).out.split('\n')[0]
|
||||||
|
if step('install') and 'install' in self.kwargs:
|
||||||
|
breakpoint()
|
||||||
|
await self.action(self.kwargs['install'], *args, **kwargs)
|
||||||
|
|
||||||
|
if step('rm') and await self.exists():
|
||||||
|
await self.exec('docker', 'rm', '-f', self.name)
|
||||||
|
|
||||||
|
if step('up'):
|
||||||
|
if await self.exists():
|
||||||
|
self.name = (await self.exec('docker', 'start', self.name)).out
|
||||||
|
else:
|
||||||
|
self.id = (await self.exec(
|
||||||
|
'docker', 'run', '-d', '--name', self.name, str(self.image))
|
||||||
|
).out
|
||||||
|
return await super().call(*args, **kwargs)
|
||||||
|
|
||||||
|
async def exists(self):
|
||||||
|
proc = await self.exec(
|
||||||
'docker', 'ps', '-aq', '--filter',
|
'docker', 'ps', '-aq', '--filter',
|
||||||
'name=' + name,
|
'name=' + self.name,
|
||||||
raises=False
|
raises=False
|
||||||
)
|
)
|
||||||
).out.split('\n')[0]
|
return bool(proc.out.strip())
|
||||||
|
|
||||||
if 'recreate' in args and self.context['ctr']:
|
|
||||||
await self.exec('docker', 'rm', '-f', self.context['ctr'])
|
|
||||||
self.context['ctr'] = None
|
|
||||||
|
|
||||||
if self.context['ctr']:
|
|
||||||
self.context['ctr'] = (await self.exec('docker', 'start', name)).out
|
|
||||||
return await super().call(*args, **kwargs)
|
|
||||||
|
|
||||||
async def copy(self, *args):
|
async def copy(self, *args):
|
||||||
src = args[:-1]
|
src = args[:-1]
|
||||||
@@ -70,7 +90,22 @@ class Docker(Localhost):
|
|||||||
else:
|
else:
|
||||||
args = ['docker', 'copy', self.ctr, s, dst]
|
args = ['docker', 'copy', self.ctr, s, dst]
|
||||||
'''
|
'''
|
||||||
args = ['docker', 'cp', s, self.context['ctr'] + ':' + dst]
|
args = ['docker', 'cp', s, self.name + ':' + dst]
|
||||||
procs.append(self.exec(*args))
|
procs.append(self.exec(*args))
|
||||||
|
|
||||||
return await asyncio.gather(*procs)
|
return await asyncio.gather(*procs)
|
||||||
|
|
||||||
|
async def up(self):
|
||||||
|
"""Ensure container is up and running."""
|
||||||
|
if await self.exists():
|
||||||
|
self.name = (await self.exec('docker', 'start', self.name)).out
|
||||||
|
else:
|
||||||
|
self.id = (await self.exec(
|
||||||
|
'docker', 'run', '-d', '--name', self.name, str(self.image))
|
||||||
|
).out
|
||||||
|
up.shlaxstep = True
|
||||||
|
|
||||||
|
async def rm(self):
|
||||||
|
"""Remove container."""
|
||||||
|
await self.exec('docker', 'rm', '-f', self.name)
|
||||||
|
rm.shlaxstep = True
|
||||||
|
|||||||
@@ -8,6 +8,11 @@ from ..strategies.script import Script
|
|||||||
|
|
||||||
class Localhost(Script):
|
class Localhost(Script):
|
||||||
root = '/'
|
root = '/'
|
||||||
|
contextualize = Script.contextualize + ['home']
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
self.home = kwargs.pop('home', os.getcwd())
|
||||||
|
|
||||||
def shargs(self, *args, **kwargs):
|
def shargs(self, *args, **kwargs):
|
||||||
user = kwargs.pop('user', None)
|
user = kwargs.pop('user', None)
|
||||||
@@ -30,9 +35,7 @@ class Localhost(Script):
|
|||||||
return args, kwargs
|
return args, kwargs
|
||||||
|
|
||||||
async def exec(self, *args, **kwargs):
|
async def exec(self, *args, **kwargs):
|
||||||
if 'debug' not in kwargs:
|
kwargs['output'] = self.output
|
||||||
kwargs['debug'] = getattr(self, 'call_kwargs', {}).get('debug', False)
|
|
||||||
kwargs.setdefault('output', self.output)
|
|
||||||
args, kwargs = self.shargs(*args, **kwargs)
|
args, kwargs = self.shargs(*args, **kwargs)
|
||||||
proc = await Proc(*args, **kwargs)()
|
proc = await Proc(*args, **kwargs)()
|
||||||
if kwargs.get('wait', True):
|
if kwargs.get('wait', True):
|
||||||
@@ -46,6 +49,9 @@ class Localhost(Script):
|
|||||||
async def env(self, name):
|
async def env(self, name):
|
||||||
return (await self.exec('echo $' + name)).out
|
return (await self.exec('echo $' + name)).out
|
||||||
|
|
||||||
|
async def exists(self, *paths):
|
||||||
|
proc = await self.exec('type ' + ' '.join(cmd), raises=False)
|
||||||
|
|
||||||
async def which(self, *cmd):
|
async def which(self, *cmd):
|
||||||
"""
|
"""
|
||||||
Return the first path to the cmd in the container.
|
Return the first path to the cmd in the container.
|
||||||
@@ -61,7 +67,10 @@ class Localhost(Script):
|
|||||||
return result
|
return result
|
||||||
|
|
||||||
async def copy(self, *args):
|
async def copy(self, *args):
|
||||||
args = ['cp', '-ra'] + list(args)
|
if args[-1].startswith('./'):
|
||||||
|
args = list(args)
|
||||||
|
args[-1] = self.home + '/' + args[-1][2:]
|
||||||
|
args = ['cp', '-rua'] + list(args)
|
||||||
return await self.exec(*args)
|
return await self.exec(*args)
|
||||||
|
|
||||||
async def mount(self, *dirs):
|
async def mount(self, *dirs):
|
||||||
|
|||||||
+1
-1
@@ -12,7 +12,7 @@ build = Buildah(
|
|||||||
'quay.io/podman/stable',
|
'quay.io/podman/stable',
|
||||||
Packages('python38', 'buildah', 'unzip', 'findutils', 'python3-yaml', upgrade=False),
|
Packages('python38', 'buildah', 'unzip', 'findutils', 'python3-yaml', upgrade=False),
|
||||||
Async(
|
Async(
|
||||||
# python3.8 on centos with pip dance ...
|
# dancing for pip on centos python3.8
|
||||||
Run('''
|
Run('''
|
||||||
curl -o setuptools.zip https://files.pythonhosted.org/packages/42/3e/2464120172859e5d103e5500315fb5555b1e908c0dacc73d80d35a9480ca/setuptools-45.1.0.zip
|
curl -o setuptools.zip https://files.pythonhosted.org/packages/42/3e/2464120172859e5d103e5500315fb5555b1e908c0dacc73d80d35a9480ca/setuptools-45.1.0.zip
|
||||||
unzip setuptools.zip
|
unzip setuptools.zip
|
||||||
|
|||||||
Reference in New Issue
Block a user