ominfra 0.0.0.dev141__py3-none-any.whl → 0.0.0.dev143__py3-none-any.whl
Sign up to get free protection for your applications and to get access to all the features.
- ominfra/manage/__init__.py +10 -0
- ominfra/manage/__main__.py +4 -0
- ominfra/manage/bootstrap.py +11 -0
- ominfra/manage/bootstrap_.py +18 -0
- ominfra/manage/commands/base.py +133 -1
- ominfra/manage/commands/execution.py +23 -0
- ominfra/manage/commands/inject.py +122 -0
- ominfra/manage/commands/marshal.py +26 -0
- ominfra/manage/commands/subprocess.py +18 -17
- ominfra/manage/config.py +10 -0
- ominfra/manage/inject.py +55 -0
- ominfra/manage/main.py +54 -132
- ominfra/manage/marshal.py +12 -0
- ominfra/manage/remote/__init__.py +0 -0
- ominfra/manage/remote/channel.py +52 -0
- ominfra/manage/remote/config.py +12 -0
- ominfra/manage/remote/execution.py +193 -0
- ominfra/manage/remote/inject.py +29 -0
- ominfra/manage/{payload.py → remote/payload.py} +7 -1
- ominfra/manage/{spawning.py → remote/spawning.py} +31 -35
- ominfra/pyremote.py +53 -14
- ominfra/scripts/journald2aws.py +216 -131
- ominfra/scripts/manage.py +2180 -452
- ominfra/scripts/supervisor.py +203 -130
- ominfra/supervisor/inject.py +2 -1
- {ominfra-0.0.0.dev141.dist-info → ominfra-0.0.0.dev143.dist-info}/METADATA +3 -3
- {ominfra-0.0.0.dev141.dist-info → ominfra-0.0.0.dev143.dist-info}/RECORD +31 -17
- {ominfra-0.0.0.dev141.dist-info → ominfra-0.0.0.dev143.dist-info}/LICENSE +0 -0
- {ominfra-0.0.0.dev141.dist-info → ominfra-0.0.0.dev143.dist-info}/WHEEL +0 -0
- {ominfra-0.0.0.dev141.dist-info → ominfra-0.0.0.dev143.dist-info}/entry_points.txt +0 -0
- {ominfra-0.0.0.dev141.dist-info → ominfra-0.0.0.dev143.dist-info}/top_level.txt +0 -0
ominfra/manage/__init__.py
CHANGED
@@ -0,0 +1,18 @@
|
|
1
|
+
from omlish.lite.inject import Injector
|
2
|
+
from omlish.lite.inject import inj
|
3
|
+
from omlish.lite.logs import configure_standard_logging
|
4
|
+
|
5
|
+
from .bootstrap import MainBootstrap
|
6
|
+
from .inject import bind_main
|
7
|
+
|
8
|
+
|
9
|
+
def main_bootstrap(bs: MainBootstrap) -> Injector:
|
10
|
+
if (log_level := bs.main_config.log_level) is not None:
|
11
|
+
configure_standard_logging(log_level)
|
12
|
+
|
13
|
+
injector = inj.create_injector(bind_main( # noqa
|
14
|
+
main_config=bs.main_config,
|
15
|
+
remote_config=bs.remote_config,
|
16
|
+
))
|
17
|
+
|
18
|
+
return injector
|
ominfra/manage/commands/base.py
CHANGED
@@ -1,8 +1,13 @@
|
|
1
1
|
# ruff: noqa: UP006 UP007
|
2
2
|
import abc
|
3
3
|
import dataclasses as dc
|
4
|
+
import logging
|
5
|
+
import traceback
|
4
6
|
import typing as ta
|
5
7
|
|
8
|
+
from omlish.lite.check import check_isinstance
|
9
|
+
from omlish.lite.strings import snake_case
|
10
|
+
|
6
11
|
|
7
12
|
CommandT = ta.TypeVar('CommandT', bound='Command')
|
8
13
|
CommandOutputT = ta.TypeVar('CommandOutputT', bound='Command.Output')
|
@@ -17,11 +22,138 @@ class Command(abc.ABC, ta.Generic[CommandOutputT]):
|
|
17
22
|
class Output(abc.ABC): # noqa
|
18
23
|
pass
|
19
24
|
|
25
|
+
@ta.final
|
26
|
+
def execute(self, executor: 'CommandExecutor') -> CommandOutputT:
|
27
|
+
return check_isinstance(executor.execute(self), self.Output) # type: ignore[return-value]
|
28
|
+
|
20
29
|
|
21
30
|
##
|
22
31
|
|
23
32
|
|
33
|
+
@dc.dataclass(frozen=True)
|
34
|
+
class CommandException:
|
35
|
+
name: str
|
36
|
+
repr: str
|
37
|
+
|
38
|
+
traceback: ta.Optional[str] = None
|
39
|
+
|
40
|
+
exc: ta.Optional[ta.Any] = None # Exception
|
41
|
+
|
42
|
+
cmd: ta.Optional[Command] = None
|
43
|
+
|
44
|
+
@classmethod
|
45
|
+
def of(
|
46
|
+
cls,
|
47
|
+
exc: Exception,
|
48
|
+
*,
|
49
|
+
omit_exc_object: bool = False,
|
50
|
+
|
51
|
+
cmd: ta.Optional[Command] = None,
|
52
|
+
) -> 'CommandException':
|
53
|
+
return CommandException(
|
54
|
+
name=type(exc).__qualname__,
|
55
|
+
repr=repr(exc),
|
56
|
+
|
57
|
+
traceback=(
|
58
|
+
''.join(traceback.format_tb(exc.__traceback__))
|
59
|
+
if getattr(exc, '__traceback__', None) is not None else None
|
60
|
+
),
|
61
|
+
|
62
|
+
exc=None if omit_exc_object else exc,
|
63
|
+
|
64
|
+
cmd=cmd,
|
65
|
+
)
|
66
|
+
|
67
|
+
|
68
|
+
class CommandOutputOrException(abc.ABC, ta.Generic[CommandOutputT]):
|
69
|
+
@property
|
70
|
+
@abc.abstractmethod
|
71
|
+
def output(self) -> ta.Optional[CommandOutputT]:
|
72
|
+
raise NotImplementedError
|
73
|
+
|
74
|
+
@property
|
75
|
+
@abc.abstractmethod
|
76
|
+
def exception(self) -> ta.Optional[CommandException]:
|
77
|
+
raise NotImplementedError
|
78
|
+
|
79
|
+
|
80
|
+
@dc.dataclass(frozen=True)
|
81
|
+
class CommandOutputOrExceptionData(CommandOutputOrException):
|
82
|
+
output: ta.Optional[Command.Output] = None
|
83
|
+
exception: ta.Optional[CommandException] = None
|
84
|
+
|
85
|
+
|
24
86
|
class CommandExecutor(abc.ABC, ta.Generic[CommandT, CommandOutputT]):
|
25
87
|
@abc.abstractmethod
|
26
|
-
def execute(self,
|
88
|
+
def execute(self, cmd: CommandT) -> CommandOutputT:
|
27
89
|
raise NotImplementedError
|
90
|
+
|
91
|
+
def try_execute(
|
92
|
+
self,
|
93
|
+
cmd: CommandT,
|
94
|
+
*,
|
95
|
+
log: ta.Optional[logging.Logger] = None,
|
96
|
+
omit_exc_object: bool = False,
|
97
|
+
) -> CommandOutputOrException[CommandOutputT]:
|
98
|
+
try:
|
99
|
+
o = self.execute(cmd)
|
100
|
+
|
101
|
+
except Exception as e: # noqa
|
102
|
+
if log is not None:
|
103
|
+
log.exception('Exception executing command: %r', type(cmd))
|
104
|
+
|
105
|
+
return CommandOutputOrExceptionData(exception=CommandException.of(
|
106
|
+
e,
|
107
|
+
omit_exc_object=omit_exc_object,
|
108
|
+
cmd=cmd,
|
109
|
+
))
|
110
|
+
|
111
|
+
else:
|
112
|
+
return CommandOutputOrExceptionData(output=o)
|
113
|
+
|
114
|
+
|
115
|
+
##
|
116
|
+
|
117
|
+
|
118
|
+
@dc.dataclass(frozen=True)
|
119
|
+
class CommandRegistration:
|
120
|
+
command_cls: ta.Type[Command]
|
121
|
+
|
122
|
+
name: ta.Optional[str] = None
|
123
|
+
|
124
|
+
@property
|
125
|
+
def name_or_default(self) -> str:
|
126
|
+
if not (cls_name := self.command_cls.__name__).endswith('Command'):
|
127
|
+
raise NameError(cls_name)
|
128
|
+
return snake_case(cls_name[:-len('Command')])
|
129
|
+
|
130
|
+
|
131
|
+
CommandRegistrations = ta.NewType('CommandRegistrations', ta.Sequence[CommandRegistration])
|
132
|
+
|
133
|
+
|
134
|
+
##
|
135
|
+
|
136
|
+
|
137
|
+
@dc.dataclass(frozen=True)
|
138
|
+
class CommandExecutorRegistration:
|
139
|
+
command_cls: ta.Type[Command]
|
140
|
+
executor_cls: ta.Type[CommandExecutor]
|
141
|
+
|
142
|
+
|
143
|
+
CommandExecutorRegistrations = ta.NewType('CommandExecutorRegistrations', ta.Sequence[CommandExecutorRegistration])
|
144
|
+
|
145
|
+
|
146
|
+
##
|
147
|
+
|
148
|
+
|
149
|
+
CommandNameMap = ta.NewType('CommandNameMap', ta.Mapping[str, ta.Type[Command]])
|
150
|
+
|
151
|
+
|
152
|
+
def build_command_name_map(crs: CommandRegistrations) -> CommandNameMap:
|
153
|
+
dct: ta.Dict[str, ta.Type[Command]] = {}
|
154
|
+
cr: CommandRegistration
|
155
|
+
for cr in crs:
|
156
|
+
if (name := cr.name_or_default) in dct:
|
157
|
+
raise NameError(name)
|
158
|
+
dct[name] = cr.command_cls
|
159
|
+
return CommandNameMap(dct)
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# ruff: noqa: UP006 UP007
|
2
|
+
import typing as ta
|
3
|
+
|
4
|
+
from .base import Command
|
5
|
+
from .base import CommandExecutor
|
6
|
+
|
7
|
+
|
8
|
+
CommandExecutorMap = ta.NewType('CommandExecutorMap', ta.Mapping[ta.Type[Command], CommandExecutor])
|
9
|
+
|
10
|
+
|
11
|
+
class CommandExecutionService(CommandExecutor):
|
12
|
+
def __init__(
|
13
|
+
self,
|
14
|
+
*,
|
15
|
+
command_executors: CommandExecutorMap,
|
16
|
+
) -> None:
|
17
|
+
super().__init__()
|
18
|
+
|
19
|
+
self._command_executors = command_executors
|
20
|
+
|
21
|
+
def execute(self, cmd: Command) -> Command.Output:
|
22
|
+
ce: CommandExecutor = self._command_executors[type(cmd)]
|
23
|
+
return ce.execute(cmd)
|
@@ -0,0 +1,122 @@
|
|
1
|
+
# ruff: noqa: UP006 UP007
|
2
|
+
import dataclasses as dc
|
3
|
+
import functools
|
4
|
+
import typing as ta
|
5
|
+
|
6
|
+
from omlish.lite.inject import Injector
|
7
|
+
from omlish.lite.inject import InjectorBindingOrBindings
|
8
|
+
from omlish.lite.inject import InjectorBindings
|
9
|
+
from omlish.lite.inject import inj
|
10
|
+
|
11
|
+
from ..config import MainConfig
|
12
|
+
from ..marshal import ObjMarshalerInstaller
|
13
|
+
from .base import Command
|
14
|
+
from .base import CommandExecutor
|
15
|
+
from .base import CommandExecutorRegistration
|
16
|
+
from .base import CommandExecutorRegistrations
|
17
|
+
from .base import CommandNameMap
|
18
|
+
from .base import CommandRegistration
|
19
|
+
from .base import CommandRegistrations
|
20
|
+
from .base import build_command_name_map
|
21
|
+
from .execution import CommandExecutionService
|
22
|
+
from .execution import CommandExecutorMap
|
23
|
+
from .marshal import install_command_marshaling
|
24
|
+
from .subprocess import SubprocessCommand
|
25
|
+
from .subprocess import SubprocessCommandExecutor
|
26
|
+
|
27
|
+
|
28
|
+
##
|
29
|
+
|
30
|
+
|
31
|
+
def bind_command(
|
32
|
+
command_cls: ta.Type[Command],
|
33
|
+
executor_cls: ta.Optional[ta.Type[CommandExecutor]],
|
34
|
+
) -> InjectorBindings:
|
35
|
+
lst: ta.List[InjectorBindingOrBindings] = [
|
36
|
+
inj.bind(CommandRegistration(command_cls), array=True),
|
37
|
+
]
|
38
|
+
|
39
|
+
if executor_cls is not None:
|
40
|
+
lst.extend([
|
41
|
+
inj.bind(executor_cls, singleton=True),
|
42
|
+
inj.bind(CommandExecutorRegistration(command_cls, executor_cls), array=True),
|
43
|
+
])
|
44
|
+
|
45
|
+
return inj.as_bindings(*lst)
|
46
|
+
|
47
|
+
|
48
|
+
##
|
49
|
+
|
50
|
+
|
51
|
+
@dc.dataclass(frozen=True)
|
52
|
+
class _FactoryCommandExecutor(CommandExecutor):
|
53
|
+
factory: ta.Callable[[], CommandExecutor]
|
54
|
+
|
55
|
+
def execute(self, i: Command) -> Command.Output:
|
56
|
+
return self.factory().execute(i)
|
57
|
+
|
58
|
+
|
59
|
+
##
|
60
|
+
|
61
|
+
|
62
|
+
def bind_commands(
|
63
|
+
*,
|
64
|
+
main_config: MainConfig,
|
65
|
+
) -> InjectorBindings:
|
66
|
+
lst: ta.List[InjectorBindingOrBindings] = [
|
67
|
+
inj.bind_array(CommandRegistration),
|
68
|
+
inj.bind_array_type(CommandRegistration, CommandRegistrations),
|
69
|
+
|
70
|
+
inj.bind_array(CommandExecutorRegistration),
|
71
|
+
inj.bind_array_type(CommandExecutorRegistration, CommandExecutorRegistrations),
|
72
|
+
|
73
|
+
inj.bind(build_command_name_map, singleton=True),
|
74
|
+
]
|
75
|
+
|
76
|
+
#
|
77
|
+
|
78
|
+
def provide_obj_marshaler_installer(cmds: CommandNameMap) -> ObjMarshalerInstaller:
|
79
|
+
return ObjMarshalerInstaller(functools.partial(install_command_marshaling, cmds))
|
80
|
+
|
81
|
+
lst.append(inj.bind(provide_obj_marshaler_installer, array=True))
|
82
|
+
|
83
|
+
#
|
84
|
+
|
85
|
+
def provide_command_executor_map(
|
86
|
+
injector: Injector,
|
87
|
+
crs: CommandExecutorRegistrations,
|
88
|
+
) -> CommandExecutorMap:
|
89
|
+
dct: ta.Dict[ta.Type[Command], CommandExecutor] = {}
|
90
|
+
|
91
|
+
cr: CommandExecutorRegistration
|
92
|
+
for cr in crs:
|
93
|
+
if cr.command_cls in dct:
|
94
|
+
raise KeyError(cr.command_cls)
|
95
|
+
|
96
|
+
factory = functools.partial(injector.provide, cr.executor_cls)
|
97
|
+
if main_config.debug:
|
98
|
+
ce = factory()
|
99
|
+
else:
|
100
|
+
ce = _FactoryCommandExecutor(factory)
|
101
|
+
|
102
|
+
dct[cr.command_cls] = ce
|
103
|
+
|
104
|
+
return CommandExecutorMap(dct)
|
105
|
+
|
106
|
+
lst.extend([
|
107
|
+
inj.bind(provide_command_executor_map, singleton=True),
|
108
|
+
|
109
|
+
inj.bind(CommandExecutionService, singleton=True, eager=main_config.debug),
|
110
|
+
inj.bind(CommandExecutor, to_key=CommandExecutionService),
|
111
|
+
])
|
112
|
+
|
113
|
+
#
|
114
|
+
|
115
|
+
for command_cls, executor_cls in [
|
116
|
+
(SubprocessCommand, SubprocessCommandExecutor),
|
117
|
+
]:
|
118
|
+
lst.append(bind_command(command_cls, executor_cls))
|
119
|
+
|
120
|
+
#
|
121
|
+
|
122
|
+
return inj.as_bindings(*lst)
|
@@ -0,0 +1,26 @@
|
|
1
|
+
from omlish.lite.marshal import ObjMarshalerManager
|
2
|
+
from omlish.lite.marshal import PolymorphicObjMarshaler
|
3
|
+
|
4
|
+
from .base import Command
|
5
|
+
from .base import CommandNameMap
|
6
|
+
|
7
|
+
|
8
|
+
def install_command_marshaling(
|
9
|
+
cmds: CommandNameMap,
|
10
|
+
msh: ObjMarshalerManager,
|
11
|
+
) -> None:
|
12
|
+
for fn in [
|
13
|
+
lambda c: c,
|
14
|
+
lambda c: c.Output,
|
15
|
+
]:
|
16
|
+
msh.register_opj_marshaler(
|
17
|
+
fn(Command),
|
18
|
+
PolymorphicObjMarshaler.of([
|
19
|
+
PolymorphicObjMarshaler.Impl(
|
20
|
+
fn(cmd),
|
21
|
+
name,
|
22
|
+
msh.get_obj_marshaler(fn(cmd)),
|
23
|
+
)
|
24
|
+
for name, cmd in cmds.items()
|
25
|
+
]),
|
26
|
+
)
|
@@ -5,6 +5,8 @@ import subprocess
|
|
5
5
|
import time
|
6
6
|
import typing as ta
|
7
7
|
|
8
|
+
from omlish.lite.subprocesses import SUBPROCESS_CHANNEL_OPTION_VALUES
|
9
|
+
from omlish.lite.subprocesses import SubprocessChannelOption
|
8
10
|
from omlish.lite.subprocesses import subprocess_maybe_shell_wrap_exec
|
9
11
|
|
10
12
|
from .base import Command
|
@@ -16,21 +18,21 @@ from .base import CommandExecutor
|
|
16
18
|
|
17
19
|
@dc.dataclass(frozen=True)
|
18
20
|
class SubprocessCommand(Command['SubprocessCommand.Output']):
|
19
|
-
|
21
|
+
cmd: ta.Sequence[str]
|
20
22
|
|
21
23
|
shell: bool = False
|
22
24
|
cwd: ta.Optional[str] = None
|
23
25
|
env: ta.Optional[ta.Mapping[str, str]] = None
|
24
26
|
|
25
|
-
|
26
|
-
|
27
|
+
stdout: str = 'pipe' # SubprocessChannelOption
|
28
|
+
stderr: str = 'pipe' # SubprocessChannelOption
|
27
29
|
|
28
30
|
input: ta.Optional[bytes] = None
|
29
31
|
timeout: ta.Optional[float] = None
|
30
32
|
|
31
33
|
def __post_init__(self) -> None:
|
32
|
-
if isinstance(self.
|
33
|
-
raise TypeError(self.
|
34
|
+
if isinstance(self.cmd, str):
|
35
|
+
raise TypeError(self.cmd)
|
34
36
|
|
35
37
|
@dc.dataclass(frozen=True)
|
36
38
|
class Output(Command.Output):
|
@@ -48,24 +50,23 @@ class SubprocessCommand(Command['SubprocessCommand.Output']):
|
|
48
50
|
|
49
51
|
class SubprocessCommandExecutor(CommandExecutor[SubprocessCommand, SubprocessCommand.Output]):
|
50
52
|
def execute(self, inp: SubprocessCommand) -> SubprocessCommand.Output:
|
51
|
-
|
52
|
-
subprocess_maybe_shell_wrap_exec(*inp.
|
53
|
+
with subprocess.Popen(
|
54
|
+
subprocess_maybe_shell_wrap_exec(*inp.cmd),
|
53
55
|
|
54
56
|
shell=inp.shell,
|
55
57
|
cwd=inp.cwd,
|
56
58
|
env={**os.environ, **(inp.env or {})},
|
57
59
|
|
58
60
|
stdin=subprocess.PIPE if inp.input is not None else None,
|
59
|
-
stdout=
|
60
|
-
stderr=
|
61
|
-
)
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
end_time = time.time()
|
61
|
+
stdout=SUBPROCESS_CHANNEL_OPTION_VALUES[ta.cast(SubprocessChannelOption, inp.stdout)],
|
62
|
+
stderr=SUBPROCESS_CHANNEL_OPTION_VALUES[ta.cast(SubprocessChannelOption, inp.stderr)],
|
63
|
+
) as proc:
|
64
|
+
start_time = time.time()
|
65
|
+
stdout, stderr = proc.communicate(
|
66
|
+
input=inp.input,
|
67
|
+
timeout=inp.timeout,
|
68
|
+
)
|
69
|
+
end_time = time.time()
|
69
70
|
|
70
71
|
return SubprocessCommand.Output(
|
71
72
|
rc=proc.returncode,
|
ominfra/manage/config.py
ADDED
ominfra/manage/inject.py
ADDED
@@ -0,0 +1,55 @@
|
|
1
|
+
# ruff: noqa: UP006 UP007
|
2
|
+
import typing as ta
|
3
|
+
|
4
|
+
from omlish.lite.inject import InjectorBindingOrBindings
|
5
|
+
from omlish.lite.inject import InjectorBindings
|
6
|
+
from omlish.lite.inject import inj
|
7
|
+
from omlish.lite.marshal import ObjMarshalerManager
|
8
|
+
|
9
|
+
from .commands.inject import bind_commands
|
10
|
+
from .config import MainConfig
|
11
|
+
from .marshal import ObjMarshalerInstaller
|
12
|
+
from .marshal import ObjMarshalerInstallers
|
13
|
+
from .remote.config import RemoteConfig
|
14
|
+
from .remote.inject import bind_remote
|
15
|
+
|
16
|
+
|
17
|
+
##
|
18
|
+
|
19
|
+
|
20
|
+
def bind_main(
|
21
|
+
*,
|
22
|
+
main_config: MainConfig,
|
23
|
+
remote_config: RemoteConfig,
|
24
|
+
) -> InjectorBindings:
|
25
|
+
lst: ta.List[InjectorBindingOrBindings] = [
|
26
|
+
inj.bind(main_config),
|
27
|
+
|
28
|
+
bind_commands(
|
29
|
+
main_config=main_config,
|
30
|
+
),
|
31
|
+
|
32
|
+
bind_remote(
|
33
|
+
remote_config=remote_config,
|
34
|
+
),
|
35
|
+
]
|
36
|
+
|
37
|
+
#
|
38
|
+
|
39
|
+
def build_obj_marshaler_manager(insts: ObjMarshalerInstallers) -> ObjMarshalerManager:
|
40
|
+
msh = ObjMarshalerManager()
|
41
|
+
inst: ObjMarshalerInstaller
|
42
|
+
for inst in insts:
|
43
|
+
inst.fn(msh)
|
44
|
+
return msh
|
45
|
+
|
46
|
+
lst.extend([
|
47
|
+
inj.bind(build_obj_marshaler_manager, singleton=True),
|
48
|
+
|
49
|
+
inj.bind_array(ObjMarshalerInstaller),
|
50
|
+
inj.bind_array_type(ObjMarshalerInstaller, ObjMarshalerInstallers),
|
51
|
+
])
|
52
|
+
|
53
|
+
#
|
54
|
+
|
55
|
+
return inj.as_bindings(*lst)
|