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/main.py
CHANGED
@@ -5,101 +5,18 @@
|
|
5
5
|
manage.py -s 'docker run -i python:3.12'
|
6
6
|
manage.py -s 'ssh -i /foo/bar.pem foo@bar.baz' -q --python=python3.8
|
7
7
|
"""
|
8
|
-
import
|
9
|
-
import
|
10
|
-
|
11
|
-
|
12
|
-
from omlish.lite.cached import static_init
|
13
|
-
from omlish.lite.json import json_dumps_compact
|
14
|
-
from omlish.lite.marshal import PolymorphicObjMarshaler
|
15
|
-
from omlish.lite.marshal import get_obj_marshaler
|
16
|
-
from omlish.lite.marshal import marshal_obj
|
17
|
-
from omlish.lite.marshal import register_opj_marshaler
|
18
|
-
from omlish.lite.marshal import unmarshal_obj
|
19
|
-
|
20
|
-
from ..pyremote import PyremoteBootstrapDriver
|
21
|
-
from ..pyremote import PyremoteBootstrapOptions
|
22
|
-
from ..pyremote import pyremote_bootstrap_finalize
|
23
|
-
from ..pyremote import pyremote_build_bootstrap_cmd
|
24
|
-
from .commands.base import Command
|
25
|
-
from .commands.subprocess import SubprocessCommand
|
26
|
-
from .commands.subprocess import SubprocessCommandExecutor
|
27
|
-
from .payload import get_payload_src
|
28
|
-
from .spawning import PySpawner
|
29
|
-
|
30
|
-
|
31
|
-
##
|
32
|
-
|
33
|
-
|
34
|
-
_COMMAND_TYPES = {
|
35
|
-
'subprocess': SubprocessCommand,
|
36
|
-
}
|
37
|
-
|
38
|
-
|
39
|
-
@static_init
|
40
|
-
def _register_command_marshaling() -> None:
|
41
|
-
for fn in [
|
42
|
-
lambda c: c,
|
43
|
-
lambda c: c.Output,
|
44
|
-
]:
|
45
|
-
register_opj_marshaler(
|
46
|
-
fn(Command),
|
47
|
-
PolymorphicObjMarshaler.of([
|
48
|
-
PolymorphicObjMarshaler.Impl(
|
49
|
-
fn(cty),
|
50
|
-
k,
|
51
|
-
get_obj_marshaler(fn(cty)),
|
52
|
-
)
|
53
|
-
for k, cty in _COMMAND_TYPES.items()
|
54
|
-
]),
|
55
|
-
)
|
56
|
-
|
57
|
-
|
58
|
-
##
|
59
|
-
|
60
|
-
|
61
|
-
def _send_obj(f: ta.IO, o: ta.Any, ty: ta.Any = None) -> None:
|
62
|
-
j = json_dumps_compact(marshal_obj(o, ty))
|
63
|
-
d = j.encode('utf-8')
|
64
|
-
|
65
|
-
f.write(struct.pack('<I', len(d)))
|
66
|
-
f.write(d)
|
67
|
-
f.flush()
|
68
|
-
|
69
|
-
|
70
|
-
def _recv_obj(f: ta.IO, ty: ta.Any) -> ta.Any:
|
71
|
-
d = f.read(4)
|
72
|
-
if not d:
|
73
|
-
return None
|
74
|
-
if len(d) != 4:
|
75
|
-
raise EOFError
|
76
|
-
|
77
|
-
sz = struct.unpack('<I', d)[0]
|
78
|
-
d = f.read(sz)
|
79
|
-
if len(d) != sz:
|
80
|
-
raise EOFError
|
81
|
-
|
82
|
-
j = json.loads(d.decode('utf-8'))
|
83
|
-
return unmarshal_obj(j, ty)
|
84
|
-
|
85
|
-
|
86
|
-
##
|
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
|
87
12
|
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
break
|
96
|
-
|
97
|
-
if isinstance(i, SubprocessCommand):
|
98
|
-
o = SubprocessCommandExecutor().execute(i) # noqa
|
99
|
-
else:
|
100
|
-
raise TypeError(i)
|
101
|
-
|
102
|
-
_send_obj(rt.output, o, Command.Output)
|
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
|
103
20
|
|
104
21
|
|
105
22
|
##
|
@@ -110,65 +27,70 @@ def _main() -> None:
|
|
110
27
|
|
111
28
|
parser = argparse.ArgumentParser()
|
112
29
|
|
30
|
+
parser.add_argument('--_payload-file')
|
31
|
+
|
113
32
|
parser.add_argument('-s', '--shell')
|
114
33
|
parser.add_argument('-q', '--shell-quote', action='store_true')
|
115
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
|
+
|
116
40
|
parser.add_argument('--debug', action='store_true')
|
117
|
-
|
41
|
+
|
42
|
+
parser.add_argument('command', nargs='+')
|
118
43
|
|
119
44
|
args = parser.parse_args()
|
120
45
|
|
121
46
|
#
|
122
47
|
|
123
|
-
|
48
|
+
bs = MainBootstrap(
|
49
|
+
main_config=MainConfig(
|
50
|
+
log_level='DEBUG' if args.debug else 'INFO',
|
124
51
|
|
125
|
-
|
52
|
+
debug=bool(args.debug),
|
53
|
+
),
|
54
|
+
|
55
|
+
remote_config=RemoteConfig(
|
56
|
+
payload_file=args._payload_file, # noqa
|
126
57
|
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
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
|
+
)
|
132
69
|
|
133
70
|
#
|
134
71
|
|
135
|
-
|
72
|
+
cmds = [
|
73
|
+
SubprocessCommand([c])
|
74
|
+
for c in args.command
|
75
|
+
]
|
136
76
|
|
137
77
|
#
|
138
78
|
|
139
|
-
|
140
|
-
bs_src,
|
79
|
+
tgt = RemoteSpawning.Target(
|
141
80
|
shell=args.shell,
|
142
81
|
shell_quote=args.shell_quote,
|
143
82
|
python=args.python,
|
144
83
|
)
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
)
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
for ci in [
|
157
|
-
SubprocessCommand(
|
158
|
-
args=['python3', '-'],
|
159
|
-
input=b'print(1)\n',
|
160
|
-
capture_stdout=True,
|
161
|
-
),
|
162
|
-
SubprocessCommand(
|
163
|
-
args=['uname'],
|
164
|
-
capture_stdout=True,
|
165
|
-
),
|
166
|
-
]:
|
167
|
-
_send_obj(proc.stdin, ci, Command)
|
168
|
-
|
169
|
-
o = _recv_obj(proc.stdout, Command.Output)
|
170
|
-
|
171
|
-
print(o)
|
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')
|
172
94
|
|
173
95
|
|
174
96
|
if __name__ == '__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])
|
File without changes
|
@@ -0,0 +1,52 @@
|
|
1
|
+
# ruff: noqa: UP006 UP007
|
2
|
+
import json
|
3
|
+
import struct
|
4
|
+
import typing as ta
|
5
|
+
|
6
|
+
from omlish.lite.json import json_dumps_compact
|
7
|
+
from omlish.lite.marshal import OBJ_MARSHALER_MANAGER
|
8
|
+
from omlish.lite.marshal import ObjMarshalerManager
|
9
|
+
|
10
|
+
|
11
|
+
T = ta.TypeVar('T')
|
12
|
+
|
13
|
+
|
14
|
+
class RemoteChannel:
|
15
|
+
def __init__(
|
16
|
+
self,
|
17
|
+
input: ta.IO, # noqa
|
18
|
+
output: ta.IO,
|
19
|
+
*,
|
20
|
+
msh: ObjMarshalerManager = OBJ_MARSHALER_MANAGER,
|
21
|
+
) -> None:
|
22
|
+
super().__init__()
|
23
|
+
|
24
|
+
self._input = input
|
25
|
+
self._output = output
|
26
|
+
self._msh = msh
|
27
|
+
|
28
|
+
def set_marshaler(self, msh: ObjMarshalerManager) -> None:
|
29
|
+
self._msh = msh
|
30
|
+
|
31
|
+
def send_obj(self, o: ta.Any, ty: ta.Any = None) -> None:
|
32
|
+
j = json_dumps_compact(self._msh.marshal_obj(o, ty))
|
33
|
+
d = j.encode('utf-8')
|
34
|
+
|
35
|
+
self._output.write(struct.pack('<I', len(d)))
|
36
|
+
self._output.write(d)
|
37
|
+
self._output.flush()
|
38
|
+
|
39
|
+
def recv_obj(self, ty: ta.Type[T]) -> ta.Optional[T]:
|
40
|
+
d = self._input.read(4)
|
41
|
+
if not d:
|
42
|
+
return None
|
43
|
+
if len(d) != 4:
|
44
|
+
raise EOFError
|
45
|
+
|
46
|
+
sz = struct.unpack('<I', d)[0]
|
47
|
+
d = self._input.read(sz)
|
48
|
+
if len(d) != sz:
|
49
|
+
raise EOFError
|
50
|
+
|
51
|
+
j = json.loads(d.decode('utf-8'))
|
52
|
+
return self._msh.unmarshal_obj(j, ty)
|
@@ -0,0 +1,12 @@
|
|
1
|
+
# ruff: noqa: UP006 UP007
|
2
|
+
import dataclasses as dc
|
3
|
+
import typing as ta
|
4
|
+
|
5
|
+
from omlish.lite.pycharm import PycharmRemoteDebug
|
6
|
+
|
7
|
+
|
8
|
+
@dc.dataclass(frozen=True)
|
9
|
+
class RemoteConfig:
|
10
|
+
payload_file: ta.Optional[str] = None
|
11
|
+
|
12
|
+
pycharm_remote_debug: ta.Optional[PycharmRemoteDebug] = None
|
@@ -0,0 +1,193 @@
|
|
1
|
+
# ruff: noqa: UP006 UP007
|
2
|
+
import contextlib
|
3
|
+
import dataclasses as dc
|
4
|
+
import logging
|
5
|
+
import typing as ta
|
6
|
+
|
7
|
+
from omlish.lite.cached import cached_nullary
|
8
|
+
from omlish.lite.check import check_not_none
|
9
|
+
from omlish.lite.logs import log
|
10
|
+
from omlish.lite.marshal import ObjMarshalerManager
|
11
|
+
from omlish.lite.pycharm import pycharm_debug_connect
|
12
|
+
|
13
|
+
from ...pyremote import PyremoteBootstrapDriver
|
14
|
+
from ...pyremote import PyremoteBootstrapOptions
|
15
|
+
from ...pyremote import pyremote_bootstrap_finalize
|
16
|
+
from ...pyremote import pyremote_build_bootstrap_cmd
|
17
|
+
from ..bootstrap import MainBootstrap
|
18
|
+
from ..commands.base import Command
|
19
|
+
from ..commands.base import CommandException
|
20
|
+
from ..commands.base import CommandExecutor
|
21
|
+
from ..commands.base import CommandOutputOrException
|
22
|
+
from ..commands.base import CommandOutputOrExceptionData
|
23
|
+
from .channel import RemoteChannel
|
24
|
+
from .payload import RemoteExecutionPayloadFile
|
25
|
+
from .payload import get_remote_payload_src
|
26
|
+
from .spawning import RemoteSpawning
|
27
|
+
|
28
|
+
|
29
|
+
if ta.TYPE_CHECKING:
|
30
|
+
from ..bootstrap_ import main_bootstrap
|
31
|
+
else:
|
32
|
+
main_bootstrap: ta.Any = None
|
33
|
+
|
34
|
+
|
35
|
+
##
|
36
|
+
|
37
|
+
|
38
|
+
def _remote_execution_main() -> None:
|
39
|
+
rt = pyremote_bootstrap_finalize() # noqa
|
40
|
+
|
41
|
+
chan = RemoteChannel(
|
42
|
+
rt.input,
|
43
|
+
rt.output,
|
44
|
+
)
|
45
|
+
|
46
|
+
bs = check_not_none(chan.recv_obj(MainBootstrap))
|
47
|
+
|
48
|
+
if (prd := bs.remote_config.pycharm_remote_debug) is not None:
|
49
|
+
pycharm_debug_connect(prd)
|
50
|
+
|
51
|
+
injector = main_bootstrap(bs)
|
52
|
+
|
53
|
+
chan.set_marshaler(injector[ObjMarshalerManager])
|
54
|
+
|
55
|
+
ce = injector[CommandExecutor]
|
56
|
+
|
57
|
+
while True:
|
58
|
+
i = chan.recv_obj(Command)
|
59
|
+
if i is None:
|
60
|
+
break
|
61
|
+
|
62
|
+
r = ce.try_execute(
|
63
|
+
i,
|
64
|
+
log=log,
|
65
|
+
omit_exc_object=True,
|
66
|
+
)
|
67
|
+
|
68
|
+
chan.send_obj(r)
|
69
|
+
|
70
|
+
|
71
|
+
##
|
72
|
+
|
73
|
+
|
74
|
+
@dc.dataclass()
|
75
|
+
class RemoteCommandError(Exception):
|
76
|
+
e: CommandException
|
77
|
+
|
78
|
+
|
79
|
+
class RemoteCommandExecutor(CommandExecutor):
|
80
|
+
def __init__(self, chan: RemoteChannel) -> None:
|
81
|
+
super().__init__()
|
82
|
+
|
83
|
+
self._chan = chan
|
84
|
+
|
85
|
+
def _remote_execute(self, cmd: Command) -> CommandOutputOrException:
|
86
|
+
self._chan.send_obj(cmd, Command)
|
87
|
+
|
88
|
+
if (r := self._chan.recv_obj(CommandOutputOrExceptionData)) is None:
|
89
|
+
raise EOFError
|
90
|
+
|
91
|
+
return r
|
92
|
+
|
93
|
+
# @ta.override
|
94
|
+
def execute(self, cmd: Command) -> Command.Output:
|
95
|
+
r = self._remote_execute(cmd)
|
96
|
+
if (e := r.exception) is not None:
|
97
|
+
raise RemoteCommandError(e)
|
98
|
+
else:
|
99
|
+
return check_not_none(r.output)
|
100
|
+
|
101
|
+
# @ta.override
|
102
|
+
def try_execute(
|
103
|
+
self,
|
104
|
+
cmd: Command,
|
105
|
+
*,
|
106
|
+
log: ta.Optional[logging.Logger] = None,
|
107
|
+
omit_exc_object: bool = False,
|
108
|
+
) -> CommandOutputOrException:
|
109
|
+
try:
|
110
|
+
r = self._remote_execute(cmd)
|
111
|
+
|
112
|
+
except Exception as e: # noqa
|
113
|
+
if log is not None:
|
114
|
+
log.exception('Exception executing remote command: %r', type(cmd))
|
115
|
+
|
116
|
+
return CommandOutputOrExceptionData(exception=CommandException.of(
|
117
|
+
e,
|
118
|
+
omit_exc_object=omit_exc_object,
|
119
|
+
cmd=cmd,
|
120
|
+
))
|
121
|
+
|
122
|
+
else:
|
123
|
+
return r
|
124
|
+
|
125
|
+
|
126
|
+
##
|
127
|
+
|
128
|
+
|
129
|
+
class RemoteExecution:
|
130
|
+
def __init__(
|
131
|
+
self,
|
132
|
+
*,
|
133
|
+
spawning: RemoteSpawning,
|
134
|
+
msh: ObjMarshalerManager,
|
135
|
+
payload_file: ta.Optional[RemoteExecutionPayloadFile] = None,
|
136
|
+
) -> None:
|
137
|
+
super().__init__()
|
138
|
+
|
139
|
+
self._spawning = spawning
|
140
|
+
self._msh = msh
|
141
|
+
self._payload_file = payload_file
|
142
|
+
|
143
|
+
#
|
144
|
+
|
145
|
+
@cached_nullary
|
146
|
+
def _payload_src(self) -> str:
|
147
|
+
return get_remote_payload_src(file=self._payload_file)
|
148
|
+
|
149
|
+
@cached_nullary
|
150
|
+
def _remote_src(self) -> ta.Sequence[str]:
|
151
|
+
return [
|
152
|
+
self._payload_src(),
|
153
|
+
'_remote_execution_main()',
|
154
|
+
]
|
155
|
+
|
156
|
+
@cached_nullary
|
157
|
+
def _spawn_src(self) -> str:
|
158
|
+
return pyremote_build_bootstrap_cmd(__package__ or 'manage')
|
159
|
+
|
160
|
+
#
|
161
|
+
|
162
|
+
@contextlib.contextmanager
|
163
|
+
def connect(
|
164
|
+
self,
|
165
|
+
tgt: RemoteSpawning.Target,
|
166
|
+
bs: MainBootstrap,
|
167
|
+
) -> ta.Generator[RemoteCommandExecutor, None, None]:
|
168
|
+
spawn_src = self._spawn_src()
|
169
|
+
remote_src = self._remote_src()
|
170
|
+
|
171
|
+
with self._spawning.spawn(
|
172
|
+
tgt,
|
173
|
+
spawn_src,
|
174
|
+
) as proc:
|
175
|
+
res = PyremoteBootstrapDriver( # noqa
|
176
|
+
remote_src,
|
177
|
+
PyremoteBootstrapOptions(
|
178
|
+
debug=bs.main_config.debug,
|
179
|
+
),
|
180
|
+
).run(
|
181
|
+
proc.stdout,
|
182
|
+
proc.stdin,
|
183
|
+
)
|
184
|
+
|
185
|
+
chan = RemoteChannel(
|
186
|
+
proc.stdout,
|
187
|
+
proc.stdin,
|
188
|
+
msh=self._msh,
|
189
|
+
)
|
190
|
+
|
191
|
+
chan.send_obj(bs)
|
192
|
+
|
193
|
+
yield RemoteCommandExecutor(chan)
|
@@ -0,0 +1,29 @@
|
|
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
|
+
|
8
|
+
from .config import RemoteConfig
|
9
|
+
from .execution import RemoteExecution
|
10
|
+
from .payload import RemoteExecutionPayloadFile
|
11
|
+
from .spawning import RemoteSpawning
|
12
|
+
|
13
|
+
|
14
|
+
def bind_remote(
|
15
|
+
*,
|
16
|
+
remote_config: RemoteConfig,
|
17
|
+
) -> InjectorBindings:
|
18
|
+
lst: ta.List[InjectorBindingOrBindings] = [
|
19
|
+
inj.bind(remote_config),
|
20
|
+
|
21
|
+
inj.bind(RemoteSpawning, singleton=True),
|
22
|
+
|
23
|
+
inj.bind(RemoteExecution, singleton=True),
|
24
|
+
]
|
25
|
+
|
26
|
+
if (pf := remote_config.payload_file) is not None:
|
27
|
+
lst.append(inj.bind(pf, to_key=RemoteExecutionPayloadFile))
|
28
|
+
|
29
|
+
return inj.as_bindings(*lst)
|
@@ -6,6 +6,9 @@ import typing as ta
|
|
6
6
|
from omlish.lite.cached import cached_nullary
|
7
7
|
|
8
8
|
|
9
|
+
RemoteExecutionPayloadFile = ta.NewType('RemoteExecutionPayloadFile', str)
|
10
|
+
|
11
|
+
|
9
12
|
@cached_nullary
|
10
13
|
def _get_self_src() -> str:
|
11
14
|
return inspect.getsource(sys.modules[__name__])
|
@@ -23,7 +26,10 @@ def _is_self_amalg() -> bool:
|
|
23
26
|
return _is_src_amalg(_get_self_src())
|
24
27
|
|
25
28
|
|
26
|
-
def
|
29
|
+
def get_remote_payload_src(
|
30
|
+
*,
|
31
|
+
file: ta.Optional[RemoteExecutionPayloadFile],
|
32
|
+
) -> str:
|
27
33
|
if file is not None:
|
28
34
|
with open(file) as f:
|
29
35
|
return f.read()
|
@@ -6,28 +6,21 @@ import subprocess
|
|
6
6
|
import typing as ta
|
7
7
|
|
8
8
|
from omlish.lite.check import check_not_none
|
9
|
+
from omlish.lite.subprocesses import SUBPROCESS_CHANNEL_OPTION_VALUES
|
10
|
+
from omlish.lite.subprocesses import SubprocessChannelOption
|
9
11
|
from omlish.lite.subprocesses import subprocess_maybe_shell_wrap_exec
|
10
12
|
|
11
13
|
|
12
|
-
class
|
13
|
-
|
14
|
+
class RemoteSpawning:
|
15
|
+
@dc.dataclass(frozen=True)
|
16
|
+
class Target:
|
17
|
+
shell: ta.Optional[str] = None
|
18
|
+
shell_quote: bool = False
|
14
19
|
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
shell: ta.Optional[str] = None,
|
20
|
-
shell_quote: bool = False,
|
21
|
-
python: str = DEFAULT_PYTHON,
|
22
|
-
stderr: ta.Optional[ta.Literal['pipe', 'stdout', 'devnull']] = None,
|
23
|
-
) -> None:
|
24
|
-
super().__init__()
|
25
|
-
|
26
|
-
self._src = src
|
27
|
-
self._shell = shell
|
28
|
-
self._shell_quote = shell_quote
|
29
|
-
self._python = python
|
30
|
-
self._stderr = stderr
|
20
|
+
DEFAULT_PYTHON: ta.ClassVar[str] = 'python3'
|
21
|
+
python: str = DEFAULT_PYTHON
|
22
|
+
|
23
|
+
stderr: ta.Optional[str] = None # SubprocessChannelOption
|
31
24
|
|
32
25
|
#
|
33
26
|
|
@@ -35,31 +28,29 @@ class PySpawner:
|
|
35
28
|
cmd: ta.Sequence[str]
|
36
29
|
shell: bool
|
37
30
|
|
38
|
-
def _prepare_cmd(
|
39
|
-
|
40
|
-
|
41
|
-
|
31
|
+
def _prepare_cmd(
|
32
|
+
self,
|
33
|
+
tgt: Target,
|
34
|
+
src: str,
|
35
|
+
) -> _PreparedCmd:
|
36
|
+
if tgt.shell is not None:
|
37
|
+
sh_src = f'{tgt.python} -c {shlex.quote(src)}'
|
38
|
+
if tgt.shell_quote:
|
42
39
|
sh_src = shlex.quote(sh_src)
|
43
|
-
sh_cmd = f'{
|
44
|
-
return
|
40
|
+
sh_cmd = f'{tgt.shell} {sh_src}'
|
41
|
+
return RemoteSpawning._PreparedCmd(
|
45
42
|
cmd=[sh_cmd],
|
46
43
|
shell=True,
|
47
44
|
)
|
48
45
|
|
49
46
|
else:
|
50
|
-
return
|
51
|
-
cmd=[
|
47
|
+
return RemoteSpawning._PreparedCmd(
|
48
|
+
cmd=[tgt.python, '-c', src],
|
52
49
|
shell=False,
|
53
50
|
)
|
54
51
|
|
55
52
|
#
|
56
53
|
|
57
|
-
_STDERR_KWARG_MAP: ta.Mapping[str, int] = {
|
58
|
-
'pipe': subprocess.PIPE,
|
59
|
-
'stdout': subprocess.STDOUT,
|
60
|
-
'devnull': subprocess.DEVNULL,
|
61
|
-
}
|
62
|
-
|
63
54
|
@dc.dataclass(frozen=True)
|
64
55
|
class Spawned:
|
65
56
|
stdin: ta.IO
|
@@ -69,23 +60,28 @@ class PySpawner:
|
|
69
60
|
@contextlib.contextmanager
|
70
61
|
def spawn(
|
71
62
|
self,
|
63
|
+
tgt: Target,
|
64
|
+
src: str,
|
72
65
|
*,
|
73
66
|
timeout: ta.Optional[float] = None,
|
74
67
|
) -> ta.Generator[Spawned, None, None]:
|
75
|
-
pc = self._prepare_cmd()
|
68
|
+
pc = self._prepare_cmd(tgt, src)
|
76
69
|
|
77
70
|
with subprocess.Popen(
|
78
71
|
subprocess_maybe_shell_wrap_exec(*pc.cmd),
|
79
72
|
shell=pc.shell,
|
80
73
|
stdin=subprocess.PIPE,
|
81
74
|
stdout=subprocess.PIPE,
|
82
|
-
stderr=
|
75
|
+
stderr=(
|
76
|
+
SUBPROCESS_CHANNEL_OPTION_VALUES[ta.cast(SubprocessChannelOption, tgt.stderr)]
|
77
|
+
if tgt.stderr is not None else None
|
78
|
+
),
|
83
79
|
) as proc:
|
84
80
|
stdin = check_not_none(proc.stdin)
|
85
81
|
stdout = check_not_none(proc.stdout)
|
86
82
|
|
87
83
|
try:
|
88
|
-
yield
|
84
|
+
yield RemoteSpawning.Spawned(
|
89
85
|
stdin=stdin,
|
90
86
|
stdout=stdout,
|
91
87
|
stderr=proc.stderr,
|