ominfra 0.0.0.dev141__tar.gz → 0.0.0.dev143__tar.gz
Sign up to get free protection for your applications and to get access to all the features.
- {ominfra-0.0.0.dev141/ominfra.egg-info → ominfra-0.0.0.dev143}/PKG-INFO +3 -3
- {ominfra-0.0.0.dev141 → ominfra-0.0.0.dev143}/ominfra/manage/__init__.py +10 -0
- ominfra-0.0.0.dev143/ominfra/manage/__main__.py +4 -0
- ominfra-0.0.0.dev143/ominfra/manage/bootstrap.py +11 -0
- ominfra-0.0.0.dev143/ominfra/manage/bootstrap_.py +18 -0
- ominfra-0.0.0.dev143/ominfra/manage/commands/base.py +159 -0
- ominfra-0.0.0.dev143/ominfra/manage/commands/execution.py +23 -0
- ominfra-0.0.0.dev143/ominfra/manage/commands/inject.py +122 -0
- ominfra-0.0.0.dev143/ominfra/manage/commands/marshal.py +26 -0
- {ominfra-0.0.0.dev141 → ominfra-0.0.0.dev143}/ominfra/manage/commands/subprocess.py +18 -17
- ominfra-0.0.0.dev143/ominfra/manage/config.py +10 -0
- ominfra-0.0.0.dev143/ominfra/manage/inject.py +55 -0
- ominfra-0.0.0.dev143/ominfra/manage/main.py +97 -0
- ominfra-0.0.0.dev143/ominfra/manage/marshal.py +12 -0
- ominfra-0.0.0.dev143/ominfra/manage/remote/channel.py +52 -0
- ominfra-0.0.0.dev143/ominfra/manage/remote/config.py +12 -0
- ominfra-0.0.0.dev143/ominfra/manage/remote/execution.py +193 -0
- ominfra-0.0.0.dev143/ominfra/manage/remote/inject.py +29 -0
- {ominfra-0.0.0.dev141/ominfra/manage → ominfra-0.0.0.dev143/ominfra/manage/remote}/payload.py +7 -1
- {ominfra-0.0.0.dev141/ominfra/manage → ominfra-0.0.0.dev143/ominfra/manage/remote}/spawning.py +31 -35
- {ominfra-0.0.0.dev141 → ominfra-0.0.0.dev143}/ominfra/pyremote.py +53 -14
- {ominfra-0.0.0.dev141 → ominfra-0.0.0.dev143}/ominfra/scripts/journald2aws.py +216 -131
- ominfra-0.0.0.dev143/ominfra/scripts/manage.py +3588 -0
- {ominfra-0.0.0.dev141 → ominfra-0.0.0.dev143}/ominfra/scripts/supervisor.py +203 -130
- {ominfra-0.0.0.dev141 → ominfra-0.0.0.dev143}/ominfra/supervisor/inject.py +2 -1
- ominfra-0.0.0.dev143/ominfra/tools/__init__.py +0 -0
- {ominfra-0.0.0.dev141 → ominfra-0.0.0.dev143/ominfra.egg-info}/PKG-INFO +3 -3
- {ominfra-0.0.0.dev141 → ominfra-0.0.0.dev143}/ominfra.egg-info/SOURCES.txt +16 -2
- {ominfra-0.0.0.dev141 → ominfra-0.0.0.dev143}/ominfra.egg-info/requires.txt +2 -2
- {ominfra-0.0.0.dev141 → ominfra-0.0.0.dev143}/pyproject.toml +3 -3
- ominfra-0.0.0.dev141/ominfra/manage/commands/base.py +0 -27
- ominfra-0.0.0.dev141/ominfra/manage/main.py +0 -175
- ominfra-0.0.0.dev141/ominfra/scripts/manage.py +0 -1860
- {ominfra-0.0.0.dev141 → ominfra-0.0.0.dev143}/LICENSE +0 -0
- {ominfra-0.0.0.dev141 → ominfra-0.0.0.dev143}/MANIFEST.in +0 -0
- {ominfra-0.0.0.dev141 → ominfra-0.0.0.dev143}/README.rst +0 -0
- {ominfra-0.0.0.dev141 → ominfra-0.0.0.dev143}/ominfra/.manifests.json +0 -0
- {ominfra-0.0.0.dev141 → ominfra-0.0.0.dev143}/ominfra/__about__.py +0 -0
- {ominfra-0.0.0.dev141 → ominfra-0.0.0.dev143}/ominfra/__init__.py +0 -0
- {ominfra-0.0.0.dev141 → ominfra-0.0.0.dev143}/ominfra/clouds/__init__.py +0 -0
- {ominfra-0.0.0.dev141 → ominfra-0.0.0.dev143}/ominfra/clouds/aws/__init__.py +0 -0
- {ominfra-0.0.0.dev141 → ominfra-0.0.0.dev143}/ominfra/clouds/aws/__main__.py +0 -0
- {ominfra-0.0.0.dev141 → ominfra-0.0.0.dev143}/ominfra/clouds/aws/auth.py +0 -0
- {ominfra-0.0.0.dev141 → ominfra-0.0.0.dev143}/ominfra/clouds/aws/cli.py +0 -0
- {ominfra-0.0.0.dev141 → ominfra-0.0.0.dev143}/ominfra/clouds/aws/dataclasses.py +0 -0
- {ominfra-0.0.0.dev141 → ominfra-0.0.0.dev143}/ominfra/clouds/aws/journald2aws/__init__.py +0 -0
- {ominfra-0.0.0.dev141 → ominfra-0.0.0.dev143}/ominfra/clouds/aws/journald2aws/__main__.py +0 -0
- {ominfra-0.0.0.dev141 → ominfra-0.0.0.dev143}/ominfra/clouds/aws/journald2aws/cursor.py +0 -0
- {ominfra-0.0.0.dev141 → ominfra-0.0.0.dev143}/ominfra/clouds/aws/journald2aws/driver.py +0 -0
- {ominfra-0.0.0.dev141 → ominfra-0.0.0.dev143}/ominfra/clouds/aws/journald2aws/main.py +0 -0
- {ominfra-0.0.0.dev141 → ominfra-0.0.0.dev143}/ominfra/clouds/aws/journald2aws/poster.py +0 -0
- {ominfra-0.0.0.dev141 → ominfra-0.0.0.dev143}/ominfra/clouds/aws/logs.py +0 -0
- {ominfra-0.0.0.dev141 → ominfra-0.0.0.dev143}/ominfra/clouds/aws/metadata.py +0 -0
- {ominfra-0.0.0.dev141 → ominfra-0.0.0.dev143}/ominfra/clouds/gcp/__init__.py +0 -0
- {ominfra-0.0.0.dev141 → ominfra-0.0.0.dev143}/ominfra/clouds/gcp/auth.py +0 -0
- {ominfra-0.0.0.dev141 → ominfra-0.0.0.dev143}/ominfra/cmds.py +0 -0
- {ominfra-0.0.0.dev141 → ominfra-0.0.0.dev143}/ominfra/configs.py +0 -0
- {ominfra-0.0.0.dev141 → ominfra-0.0.0.dev143}/ominfra/journald/__init__.py +0 -0
- {ominfra-0.0.0.dev141 → ominfra-0.0.0.dev143}/ominfra/journald/fields.py +0 -0
- {ominfra-0.0.0.dev141 → ominfra-0.0.0.dev143}/ominfra/journald/genmessages.py +0 -0
- {ominfra-0.0.0.dev141 → ominfra-0.0.0.dev143}/ominfra/journald/messages.py +0 -0
- {ominfra-0.0.0.dev141 → ominfra-0.0.0.dev143}/ominfra/journald/tailer.py +0 -0
- {ominfra-0.0.0.dev141 → ominfra-0.0.0.dev143}/ominfra/manage/commands/__init__.py +0 -0
- {ominfra-0.0.0.dev141/ominfra/scripts → ominfra-0.0.0.dev143/ominfra/manage/remote}/__init__.py +0 -0
- {ominfra-0.0.0.dev141/ominfra/supervisor/utils → ominfra-0.0.0.dev143/ominfra/scripts}/__init__.py +0 -0
- {ominfra-0.0.0.dev141 → ominfra-0.0.0.dev143}/ominfra/ssh.py +0 -0
- {ominfra-0.0.0.dev141 → ominfra-0.0.0.dev143}/ominfra/supervisor/LICENSE.txt +0 -0
- {ominfra-0.0.0.dev141 → ominfra-0.0.0.dev143}/ominfra/supervisor/__init__.py +0 -0
- {ominfra-0.0.0.dev141 → ominfra-0.0.0.dev143}/ominfra/supervisor/__main__.py +0 -0
- {ominfra-0.0.0.dev141 → ominfra-0.0.0.dev143}/ominfra/supervisor/configs.py +0 -0
- {ominfra-0.0.0.dev141 → ominfra-0.0.0.dev143}/ominfra/supervisor/dispatchers.py +0 -0
- {ominfra-0.0.0.dev141 → ominfra-0.0.0.dev143}/ominfra/supervisor/dispatchersimpl.py +0 -0
- {ominfra-0.0.0.dev141 → ominfra-0.0.0.dev143}/ominfra/supervisor/events.py +0 -0
- {ominfra-0.0.0.dev141 → ominfra-0.0.0.dev143}/ominfra/supervisor/exceptions.py +0 -0
- {ominfra-0.0.0.dev141 → ominfra-0.0.0.dev143}/ominfra/supervisor/groups.py +0 -0
- {ominfra-0.0.0.dev141 → ominfra-0.0.0.dev143}/ominfra/supervisor/groupsimpl.py +0 -0
- {ominfra-0.0.0.dev141 → ominfra-0.0.0.dev143}/ominfra/supervisor/http.py +0 -0
- {ominfra-0.0.0.dev141 → ominfra-0.0.0.dev143}/ominfra/supervisor/io.py +0 -0
- {ominfra-0.0.0.dev141 → ominfra-0.0.0.dev143}/ominfra/supervisor/main.py +0 -0
- {ominfra-0.0.0.dev141 → ominfra-0.0.0.dev143}/ominfra/supervisor/pipes.py +0 -0
- {ominfra-0.0.0.dev141 → ominfra-0.0.0.dev143}/ominfra/supervisor/privileges.py +0 -0
- {ominfra-0.0.0.dev141 → ominfra-0.0.0.dev143}/ominfra/supervisor/process.py +0 -0
- {ominfra-0.0.0.dev141 → ominfra-0.0.0.dev143}/ominfra/supervisor/processimpl.py +0 -0
- {ominfra-0.0.0.dev141 → ominfra-0.0.0.dev143}/ominfra/supervisor/setup.py +0 -0
- {ominfra-0.0.0.dev141 → ominfra-0.0.0.dev143}/ominfra/supervisor/setupimpl.py +0 -0
- {ominfra-0.0.0.dev141 → ominfra-0.0.0.dev143}/ominfra/supervisor/signals.py +0 -0
- {ominfra-0.0.0.dev141 → ominfra-0.0.0.dev143}/ominfra/supervisor/spawning.py +0 -0
- {ominfra-0.0.0.dev141 → ominfra-0.0.0.dev143}/ominfra/supervisor/spawningimpl.py +0 -0
- {ominfra-0.0.0.dev141 → ominfra-0.0.0.dev143}/ominfra/supervisor/states.py +0 -0
- {ominfra-0.0.0.dev141 → ominfra-0.0.0.dev143}/ominfra/supervisor/supervisor.py +0 -0
- {ominfra-0.0.0.dev141 → ominfra-0.0.0.dev143}/ominfra/supervisor/types.py +0 -0
- {ominfra-0.0.0.dev141/ominfra/tailscale → ominfra-0.0.0.dev143/ominfra/supervisor/utils}/__init__.py +0 -0
- {ominfra-0.0.0.dev141 → ominfra-0.0.0.dev143}/ominfra/supervisor/utils/collections.py +0 -0
- {ominfra-0.0.0.dev141 → ominfra-0.0.0.dev143}/ominfra/supervisor/utils/diag.py +0 -0
- {ominfra-0.0.0.dev141 → ominfra-0.0.0.dev143}/ominfra/supervisor/utils/fds.py +0 -0
- {ominfra-0.0.0.dev141 → ominfra-0.0.0.dev143}/ominfra/supervisor/utils/fs.py +0 -0
- {ominfra-0.0.0.dev141 → ominfra-0.0.0.dev143}/ominfra/supervisor/utils/os.py +0 -0
- {ominfra-0.0.0.dev141 → ominfra-0.0.0.dev143}/ominfra/supervisor/utils/ostypes.py +0 -0
- {ominfra-0.0.0.dev141 → ominfra-0.0.0.dev143}/ominfra/supervisor/utils/signals.py +0 -0
- {ominfra-0.0.0.dev141 → ominfra-0.0.0.dev143}/ominfra/supervisor/utils/strings.py +0 -0
- {ominfra-0.0.0.dev141 → ominfra-0.0.0.dev143}/ominfra/supervisor/utils/users.py +0 -0
- {ominfra-0.0.0.dev141/ominfra/tools → ominfra-0.0.0.dev143/ominfra/tailscale}/__init__.py +0 -0
- {ominfra-0.0.0.dev141 → ominfra-0.0.0.dev143}/ominfra/tailscale/api.py +0 -0
- {ominfra-0.0.0.dev141 → ominfra-0.0.0.dev143}/ominfra/tailscale/cli.py +0 -0
- {ominfra-0.0.0.dev141 → ominfra-0.0.0.dev143}/ominfra/threadworkers.py +0 -0
- {ominfra-0.0.0.dev141 → ominfra-0.0.0.dev143}/ominfra/tools/listresources.py +0 -0
- {ominfra-0.0.0.dev141 → ominfra-0.0.0.dev143}/ominfra.egg-info/dependency_links.txt +0 -0
- {ominfra-0.0.0.dev141 → ominfra-0.0.0.dev143}/ominfra.egg-info/entry_points.txt +0 -0
- {ominfra-0.0.0.dev141 → ominfra-0.0.0.dev143}/ominfra.egg-info/top_level.txt +0 -0
- {ominfra-0.0.0.dev141 → ominfra-0.0.0.dev143}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: ominfra
|
3
|
-
Version: 0.0.0.
|
3
|
+
Version: 0.0.0.dev143
|
4
4
|
Summary: ominfra
|
5
5
|
Author: wrmsr
|
6
6
|
License: BSD-3-Clause
|
@@ -12,8 +12,8 @@ Classifier: Operating System :: OS Independent
|
|
12
12
|
Classifier: Operating System :: POSIX
|
13
13
|
Requires-Python: >=3.12
|
14
14
|
License-File: LICENSE
|
15
|
-
Requires-Dist: omdev==0.0.0.
|
16
|
-
Requires-Dist: omlish==0.0.0.
|
15
|
+
Requires-Dist: omdev==0.0.0.dev143
|
16
|
+
Requires-Dist: omlish==0.0.0.dev143
|
17
17
|
Provides-Extra: all
|
18
18
|
Requires-Dist: paramiko~=3.5; extra == "all"
|
19
19
|
Requires-Dist: asyncssh~=2.18; extra == "all"
|
@@ -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
|
@@ -0,0 +1,159 @@
|
|
1
|
+
# ruff: noqa: UP006 UP007
|
2
|
+
import abc
|
3
|
+
import dataclasses as dc
|
4
|
+
import logging
|
5
|
+
import traceback
|
6
|
+
import typing as ta
|
7
|
+
|
8
|
+
from omlish.lite.check import check_isinstance
|
9
|
+
from omlish.lite.strings import snake_case
|
10
|
+
|
11
|
+
|
12
|
+
CommandT = ta.TypeVar('CommandT', bound='Command')
|
13
|
+
CommandOutputT = ta.TypeVar('CommandOutputT', bound='Command.Output')
|
14
|
+
|
15
|
+
|
16
|
+
##
|
17
|
+
|
18
|
+
|
19
|
+
@dc.dataclass(frozen=True)
|
20
|
+
class Command(abc.ABC, ta.Generic[CommandOutputT]):
|
21
|
+
@dc.dataclass(frozen=True)
|
22
|
+
class Output(abc.ABC): # noqa
|
23
|
+
pass
|
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
|
+
|
29
|
+
|
30
|
+
##
|
31
|
+
|
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
|
+
|
86
|
+
class CommandExecutor(abc.ABC, ta.Generic[CommandT, CommandOutputT]):
|
87
|
+
@abc.abstractmethod
|
88
|
+
def execute(self, cmd: CommandT) -> CommandOutputT:
|
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,
|
@@ -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)
|
@@ -0,0 +1,97 @@
|
|
1
|
+
#!/usr/bin/env python3
|
2
|
+
# @omlish-amalg ../scripts/manage.py
|
3
|
+
# ruff: noqa: UP006 UP007
|
4
|
+
"""
|
5
|
+
manage.py -s 'docker run -i python:3.12'
|
6
|
+
manage.py -s 'ssh -i /foo/bar.pem foo@bar.baz' -q --python=python3.8
|
7
|
+
"""
|
8
|
+
from omlish.lite.logs import log # noqa
|
9
|
+
from omlish.lite.marshal import ObjMarshalOptions
|
10
|
+
from omlish.lite.marshal import ObjMarshalerManager
|
11
|
+
from omlish.lite.pycharm import PycharmRemoteDebug
|
12
|
+
|
13
|
+
from .bootstrap import MainBootstrap
|
14
|
+
from .bootstrap_ import main_bootstrap
|
15
|
+
from .commands.subprocess import SubprocessCommand
|
16
|
+
from .config import MainConfig
|
17
|
+
from .remote.config import RemoteConfig
|
18
|
+
from .remote.execution import RemoteExecution
|
19
|
+
from .remote.spawning import RemoteSpawning
|
20
|
+
|
21
|
+
|
22
|
+
##
|
23
|
+
|
24
|
+
|
25
|
+
def _main() -> None:
|
26
|
+
import argparse
|
27
|
+
|
28
|
+
parser = argparse.ArgumentParser()
|
29
|
+
|
30
|
+
parser.add_argument('--_payload-file')
|
31
|
+
|
32
|
+
parser.add_argument('-s', '--shell')
|
33
|
+
parser.add_argument('-q', '--shell-quote', action='store_true')
|
34
|
+
parser.add_argument('--python', default='python3')
|
35
|
+
|
36
|
+
parser.add_argument('--pycharm-debug-port', type=int)
|
37
|
+
parser.add_argument('--pycharm-debug-host')
|
38
|
+
parser.add_argument('--pycharm-debug-version')
|
39
|
+
|
40
|
+
parser.add_argument('--debug', action='store_true')
|
41
|
+
|
42
|
+
parser.add_argument('command', nargs='+')
|
43
|
+
|
44
|
+
args = parser.parse_args()
|
45
|
+
|
46
|
+
#
|
47
|
+
|
48
|
+
bs = MainBootstrap(
|
49
|
+
main_config=MainConfig(
|
50
|
+
log_level='DEBUG' if args.debug else 'INFO',
|
51
|
+
|
52
|
+
debug=bool(args.debug),
|
53
|
+
),
|
54
|
+
|
55
|
+
remote_config=RemoteConfig(
|
56
|
+
payload_file=args._payload_file, # noqa
|
57
|
+
|
58
|
+
pycharm_remote_debug=PycharmRemoteDebug(
|
59
|
+
port=args.pycharm_debug_port,
|
60
|
+
host=args.pycharm_debug_host,
|
61
|
+
install_version=args.pycharm_debug_version,
|
62
|
+
) if args.pycharm_debug_port is not None else None,
|
63
|
+
),
|
64
|
+
)
|
65
|
+
|
66
|
+
injector = main_bootstrap(
|
67
|
+
bs,
|
68
|
+
)
|
69
|
+
|
70
|
+
#
|
71
|
+
|
72
|
+
cmds = [
|
73
|
+
SubprocessCommand([c])
|
74
|
+
for c in args.command
|
75
|
+
]
|
76
|
+
|
77
|
+
#
|
78
|
+
|
79
|
+
tgt = RemoteSpawning.Target(
|
80
|
+
shell=args.shell,
|
81
|
+
shell_quote=args.shell_quote,
|
82
|
+
python=args.python,
|
83
|
+
)
|
84
|
+
|
85
|
+
with injector[RemoteExecution].connect(tgt, bs) as rce:
|
86
|
+
for cmd in cmds:
|
87
|
+
r = rce.try_execute(cmd)
|
88
|
+
|
89
|
+
print(injector[ObjMarshalerManager].marshal_obj(r, opts=ObjMarshalOptions(raw_bytes=True)))
|
90
|
+
|
91
|
+
#
|
92
|
+
|
93
|
+
print('Success')
|
94
|
+
|
95
|
+
|
96
|
+
if __name__ == '__main__':
|
97
|
+
_main()
|
@@ -0,0 +1,12 @@
|
|
1
|
+
import dataclasses as dc
|
2
|
+
import typing as ta
|
3
|
+
|
4
|
+
from omlish.lite.marshal import ObjMarshalerManager
|
5
|
+
|
6
|
+
|
7
|
+
@dc.dataclass(frozen=True)
|
8
|
+
class ObjMarshalerInstaller:
|
9
|
+
fn: ta.Callable[[ObjMarshalerManager], None]
|
10
|
+
|
11
|
+
|
12
|
+
ObjMarshalerInstallers = ta.NewType('ObjMarshalerInstallers', ta.Sequence[ObjMarshalerInstaller])
|