Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6b15838059 | ||
|
|
85e11755f7 | ||
|
|
ef7656ddea | ||
|
|
d16a761241 |
+16
-4
@@ -9,11 +9,10 @@ from ..exceptions import WrongResult
|
|||||||
from ..result import Result
|
from ..result import Result
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class class_or_instance_method:
|
class class_or_instance_method:
|
||||||
def __init__(self, f):
|
def __init__(self, f):
|
||||||
self.f = f
|
self.f = f
|
||||||
|
|
||||||
def __get__(self, instance, owner):
|
def __get__(self, instance, owner):
|
||||||
def newfunc(*args, **kwargs):
|
def newfunc(*args, **kwargs):
|
||||||
return self.f(
|
return self.f(
|
||||||
@@ -43,10 +42,17 @@ class Action:
|
|||||||
'''.strip(),
|
'''.strip(),
|
||||||
immediate=True,
|
immediate=True,
|
||||||
),
|
),
|
||||||
|
verbose=dict(
|
||||||
|
alias='v',
|
||||||
|
default=False,
|
||||||
|
help='Verbose, like -d=visit,cmd,out',
|
||||||
|
immediate=True,
|
||||||
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
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():
|
for key, value in kwargs.items():
|
||||||
setattr(self, key, value)
|
setattr(self, key, value)
|
||||||
if isinstance(value, Action):
|
if isinstance(value, Action):
|
||||||
@@ -110,7 +116,10 @@ class Action:
|
|||||||
from ..targets.localhost import Localhost
|
from ..targets.localhost import Localhost
|
||||||
targets = [Localhost()]
|
targets = [Localhost()]
|
||||||
|
|
||||||
output = Output(regexp=self.regexps, debug=True)
|
output = Output(
|
||||||
|
regexp=self.regexps,
|
||||||
|
debug='cmd,visit,out' if options['verbose'] else options['debug'],
|
||||||
|
)
|
||||||
results = []
|
results = []
|
||||||
for target in targets:
|
for target in targets:
|
||||||
target.output = output
|
target.output = output
|
||||||
@@ -130,7 +139,10 @@ class Action:
|
|||||||
action.step = step
|
action.step = step
|
||||||
output.start(action)
|
output.start(action)
|
||||||
try:
|
try:
|
||||||
await getattr(action, step)()
|
if isinstance(getattr(action, step), Action):
|
||||||
|
await getattr(action, step)(**options)
|
||||||
|
else:
|
||||||
|
await getattr(action, step)()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
output.fail(action, e)
|
output.fail(action, e)
|
||||||
action.result.status = 'fail'
|
action.result.status = 'fail'
|
||||||
|
|||||||
@@ -94,7 +94,7 @@ class Packages(Action):
|
|||||||
f.write(str(os.getpid()))
|
f.write(str(os.getpid()))
|
||||||
|
|
||||||
try:
|
try:
|
||||||
await self.rexec(self.cmds['update'])
|
await self.target.rexec(self.cmds['update'])
|
||||||
finally:
|
finally:
|
||||||
os.unlink(lockfile)
|
os.unlink(lockfile)
|
||||||
|
|
||||||
@@ -102,7 +102,7 @@ class Packages(Action):
|
|||||||
f.write(str(now))
|
f.write(str(now))
|
||||||
else:
|
else:
|
||||||
while os.path.exists(lockfile):
|
while os.path.exists(lockfile):
|
||||||
print(f'{self.container.name} | Waiting for update ...')
|
print(f'{self.target} | Waiting for {lockfile} ...')
|
||||||
await asyncio.sleep(1)
|
await asyncio.sleep(1)
|
||||||
|
|
||||||
async def apply(self):
|
async def apply(self):
|
||||||
|
|||||||
+11
-5
@@ -3,14 +3,20 @@ from .base import Action
|
|||||||
|
|
||||||
class Run(Action):
|
class Run(Action):
|
||||||
"""Run a script or command on a target."""
|
"""Run a script or command on a target."""
|
||||||
async def call(self, *args, **kwargs):
|
def __init__(self, *args, image=None, **kwargs):
|
||||||
image = self.kwargs.get('image', None)
|
super().__init__(**kwargs)
|
||||||
if not image:
|
self.args = args
|
||||||
return await self.exec(*self.args, **self.kwargs)
|
self.kwargs = kwargs
|
||||||
|
self.image = image
|
||||||
|
|
||||||
|
async def apply(self):
|
||||||
|
if not self.image:
|
||||||
|
return await self.target.exec(*self.args, **self.kwargs)
|
||||||
|
|
||||||
from ..targets.buildah import Buildah
|
from ..targets.buildah import Buildah
|
||||||
from ..targets.docker import Docker
|
from ..targets.docker import Docker
|
||||||
|
|
||||||
if isinstance(image, Buildah):
|
if isinstance(image, Buildah):
|
||||||
breakpoint()
|
|
||||||
result = await self.action(image, *args, **kwargs)
|
result = await self.action(image, *args, **kwargs)
|
||||||
|
|
||||||
return await Docker(
|
return await Docker(
|
||||||
|
|||||||
+87
-11
@@ -46,20 +46,53 @@ class ConsoleScript(cli2.ConsoleScript):
|
|||||||
super().append(arg)
|
super().append(arg)
|
||||||
|
|
||||||
def __call__(self):
|
def __call__(self):
|
||||||
if len(sys.argv) > 1 and os.path.exists(sys.argv[1]):
|
if len(self.argv) > 1 and os.path.exists(self.argv[1]):
|
||||||
pass
|
self.argv = sys.argv[1:]
|
||||||
|
|
||||||
|
spec = importlib.util.spec_from_file_location('shlaxfile', sys.argv[1])
|
||||||
|
mod = importlib.util.module_from_spec(spec)
|
||||||
|
spec.loader.exec_module(mod)
|
||||||
|
self.doc = (inspect.getdoc(mod) or '').split("\n")[0]
|
||||||
|
for name, value in mod.__dict__.items():
|
||||||
|
if isinstance(value, Action):
|
||||||
|
self[name] = cli2.Callable(
|
||||||
|
name,
|
||||||
|
self.action(value),
|
||||||
|
doc=type(value).__doc__,
|
||||||
|
options={
|
||||||
|
option: cli2.Option(option, **cfg)
|
||||||
|
for option, cfg in value.options.items()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
#self[name] = value
|
||||||
|
#elif callable(value) and getattr(value, '__name__', '').startswith('test_'):
|
||||||
|
# self.tests[value.__name__] = value
|
||||||
|
|
||||||
|
#modname = sys.argv[1].split('/')[-1].replace('.py', '')
|
||||||
|
#mod = importlib.import_module('shlax.actions.' + modname)
|
||||||
else:
|
else:
|
||||||
scripts = glob.glob(os.path.join(
|
scripts = glob.glob(os.path.join(
|
||||||
os.path.dirname(__file__), 'actions', '*.py'))
|
os.path.dirname(__file__), 'actions', '*.py'))
|
||||||
for script in scripts:
|
for script in scripts:
|
||||||
modname = script.split('/')[-1].replace('.py', '')
|
modname = script.split('/')[-1].replace('.py', '')
|
||||||
|
if modname == '__init__':
|
||||||
|
continue
|
||||||
|
|
||||||
mod = importlib.import_module('shlax.actions.' + modname)
|
mod = importlib.import_module('shlax.actions.' + modname)
|
||||||
for key, value in mod.__dict__.items():
|
for key, value in mod.__dict__.items():
|
||||||
|
if key == '__builtins__':
|
||||||
|
continue
|
||||||
if key.lower() != modname:
|
if key.lower() != modname:
|
||||||
continue
|
continue
|
||||||
break
|
break
|
||||||
self[modname] = cli2.Callable(
|
self[modname] = cli2.Callable(
|
||||||
modname, self.action_class(value))
|
modname,
|
||||||
|
self.action_class(value),
|
||||||
|
options={
|
||||||
|
option: cli2.Option(option, **cfg)
|
||||||
|
for option, cfg in value.options.items()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
scripts = glob.glob(os.path.join(
|
scripts = glob.glob(os.path.join(
|
||||||
os.path.dirname(__file__), 'repo', '*.py'))
|
os.path.dirname(__file__), 'repo', '*.py'))
|
||||||
@@ -74,22 +107,63 @@ class ConsoleScript(cli2.ConsoleScript):
|
|||||||
if key == 'main':
|
if key == 'main':
|
||||||
if len(value.steps()) == 1:
|
if len(value.steps()) == 1:
|
||||||
self[modname] = cli2.Callable(
|
self[modname] = cli2.Callable(
|
||||||
modname, self.action(value), doc=doc)
|
modname,
|
||||||
|
self.action(value),
|
||||||
|
doc=doc,
|
||||||
|
options={
|
||||||
|
option: cli2.Option(option, **cfg)
|
||||||
|
for option, cfg in value.options.items()
|
||||||
|
}
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
for name, method in value.steps().items():
|
for name, step in value.steps().items():
|
||||||
self[modname][name] = cli2.Callable(
|
if isinstance(step, Action):
|
||||||
modname, self.action(value),
|
self[modname][name] = cli2.Callable(
|
||||||
doc=inspect.getdoc(method)
|
modname,
|
||||||
)
|
self.action(step),
|
||||||
|
doc=inspect.getdoc(step),
|
||||||
|
options={
|
||||||
|
option: cli2.Option(option, **cfg)
|
||||||
|
for option, cfg in value.options.items()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
# should be a method, just clone the
|
||||||
|
# original action and replace default_steps
|
||||||
|
action = copy.deepcopy(value)
|
||||||
|
action.default_steps = [name]
|
||||||
|
self[modname][name] = cli2.Callable(
|
||||||
|
modname,
|
||||||
|
self.action(action),
|
||||||
|
doc=inspect.getdoc(step),
|
||||||
|
options={
|
||||||
|
option: cli2.Option(option, **cfg)
|
||||||
|
for option, cfg in value.options.items()
|
||||||
|
}
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
if len(value.steps()) == 1:
|
if len(value.steps()) == 1:
|
||||||
self[modname][key] = cli2.Callable(
|
self[modname][key] = cli2.Callable(
|
||||||
modname, self.action(value), doc=doc)
|
modname,
|
||||||
|
self.action(value),
|
||||||
|
doc=doc,
|
||||||
|
options={
|
||||||
|
option: cli2.Option(option, **cfg)
|
||||||
|
for option, cfg in value.options.items()
|
||||||
|
}
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
self[modname][key] = cli2.Group('steps')
|
self[modname][key] = cli2.Group('steps')
|
||||||
for step in value.steps():
|
for step in value.steps():
|
||||||
self[modname][key][step] = cli2.Callable(
|
self[modname][key][step] = cli2.Callable(
|
||||||
modname, self.action(value), doc='lol')
|
modname,
|
||||||
|
self.action(value),
|
||||||
|
doc='lol',
|
||||||
|
options={
|
||||||
|
option: cli2.Option(option, **cfg)
|
||||||
|
for option, cfg in value.options.items()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
return super().__call__()
|
return super().__call__()
|
||||||
|
|
||||||
@@ -101,6 +175,7 @@ class ConsoleScript(cli2.ConsoleScript):
|
|||||||
# ??? gotta be missing something, commenting meanwhile
|
# ??? gotta be missing something, commenting meanwhile
|
||||||
# action = copy.deepcopy(action)
|
# action = copy.deepcopy(action)
|
||||||
return await action(*self.parser.targets, **options)
|
return await action(*self.parser.targets, **options)
|
||||||
|
cb.__name__ = type(action).__name__
|
||||||
return cb
|
return cb
|
||||||
|
|
||||||
def action_class(self, action_class):
|
def action_class(self, action_class):
|
||||||
@@ -155,6 +230,7 @@ class ConsoleScript(cli2.ConsoleScript):
|
|||||||
options.update(self.parser.options)
|
options.update(self.parser.options)
|
||||||
return await action_class(*_args, **kwargs)(*self.parser.targets, **options)
|
return await action_class(*_args, **kwargs)(*self.parser.targets, **options)
|
||||||
cb.__doc__ = (inspect.getdoc(action_class) or '').split("\n")[0]
|
cb.__doc__ = (inspect.getdoc(action_class) or '').split("\n")[0]
|
||||||
|
cb.__name__ = action_class.__name__
|
||||||
return cb
|
return cb
|
||||||
|
|
||||||
def call(self, command):
|
def call(self, command):
|
||||||
|
|||||||
+2
-2
@@ -12,10 +12,10 @@ class WrongResult(ShlaxException):
|
|||||||
|
|
||||||
msg = f'FAIL exit with {proc.rc} ' + proc.args[0]
|
msg = f'FAIL exit with {proc.rc} ' + proc.args[0]
|
||||||
|
|
||||||
if not proc.debug or 'cmd' not in str(proc.debug):
|
if not proc.output.debug or 'cmd' not in str(proc.output.debug):
|
||||||
msg += '\n' + proc.cmd
|
msg += '\n' + proc.cmd
|
||||||
|
|
||||||
if not proc.debug or 'out' not in str(proc.debug):
|
if not proc.output.debug or 'out' not in str(proc.output.debug):
|
||||||
msg += '\n' + proc.out
|
msg += '\n' + proc.out
|
||||||
msg += '\n' + proc.err
|
msg += '\n' + proc.err
|
||||||
|
|
||||||
|
|||||||
+2
-3
@@ -54,8 +54,7 @@ class Proc:
|
|||||||
"""
|
"""
|
||||||
test = False
|
test = False
|
||||||
|
|
||||||
def __init__(self, *args, prefix=None, raises=True, debug=None, output=None):
|
def __init__(self, *args, prefix=None, raises=True, output=None):
|
||||||
self.debug = debug if not self.test else False
|
|
||||||
self.output = output or Output()
|
self.output = output or Output()
|
||||||
self.cmd = ' '.join(args)
|
self.cmd = ' '.join(args)
|
||||||
self.args = args
|
self.args = args
|
||||||
@@ -87,7 +86,7 @@ class Proc:
|
|||||||
if self.called:
|
if self.called:
|
||||||
raise Exception('Already called: ' + self.cmd)
|
raise Exception('Already called: ' + self.cmd)
|
||||||
|
|
||||||
if self.debug is True or 'cmd' in str(self.debug):
|
if 'cmd' in str(self.output.debug):
|
||||||
self.output.cmd(self.cmd)
|
self.output.cmd(self.cmd)
|
||||||
|
|
||||||
if self.test:
|
if self.test:
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ from .localhost import Localhost
|
|||||||
class Docker(Localhost):
|
class Docker(Localhost):
|
||||||
"""Manage a docker container."""
|
"""Manage a docker container."""
|
||||||
default_steps = ['install', 'up']
|
default_steps = ['install', 'up']
|
||||||
|
contextualize = ['image', 'home']
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
self.image = kwargs.get('image', 'alpine')
|
self.image = kwargs.get('image', 'alpine')
|
||||||
@@ -53,7 +54,6 @@ class Docker(Localhost):
|
|||||||
# )
|
# )
|
||||||
# ).out.split('\n')[0]
|
# ).out.split('\n')[0]
|
||||||
if step('install') and 'install' in self.kwargs:
|
if step('install') and 'install' in self.kwargs:
|
||||||
breakpoint()
|
|
||||||
await self.action(self.kwargs['install'], *args, **kwargs)
|
await self.action(self.kwargs['install'], *args, **kwargs)
|
||||||
|
|
||||||
if step('rm') and await self.exists():
|
if step('rm') and await self.exists():
|
||||||
|
|||||||
@@ -0,0 +1,7 @@
|
|||||||
|
from shlax.cli import ConsoleScript
|
||||||
|
|
||||||
|
|
||||||
|
def test_parser():
|
||||||
|
parser = ConsoleScript.Parser(['@host'])
|
||||||
|
parser.parse()
|
||||||
|
assert parser.targets['host'] == Ssh('host')
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
from shlax.cli import ConsoleScript
|
||||||
|
|
||||||
|
|
||||||
|
def test_parser():
|
||||||
|
parser = ConsoleScript.Parser(['@host'])
|
||||||
|
parser.parse()
|
||||||
|
assert parser.targets['host'] == Ssh('host')
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
from shlax.play import Play
|
||||||
|
|
||||||
|
|
||||||
|
def test_play_call():
|
||||||
|
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
import copy
|
||||||
|
|
||||||
|
|
||||||
|
class Action:
|
||||||
|
args = dict(
|
||||||
|
step=None,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class
|
||||||
|
user=dict(
|
||||||
|
doc='Username',
|
||||||
|
required=True,
|
||||||
|
),
|
||||||
|
steps=dict(
|
||||||
|
up='Started',
|
||||||
|
down='Stopped',
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
self.args = args
|
||||||
|
self.kwargs = kwargs
|
||||||
|
|
||||||
|
def __call__(self, *args, **kwargs):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class Target(Action):
|
||||||
|
def __call__(self, action):
|
||||||
|
action = copy.deepcopy(action)
|
||||||
|
action.target = self
|
||||||
|
|
||||||
|
|
||||||
|
class FakeAction(Action):
|
||||||
|
|
||||||
|
|
||||||
|
def __init__(self, user, path, *steps, **kwargs)
|
||||||
|
self.user = user
|
||||||
|
self.path = path
|
||||||
|
self.steps = steps
|
||||||
|
self.kwargs = kwargs
|
||||||
|
|
||||||
|
|
||||||
|
action = Action('root', '/test', 'up', 'rm')
|
||||||
|
target = Target()
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
if not os.getenv('CI'):
|
||||||
|
pytest.skip('Please run with ./shlaxfile.py test', allow_module_level=True)
|
||||||
Reference in New Issue
Block a user