ominfra 0.0.0.dev160__py3-none-any.whl → 0.0.0.dev161__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- ominfra/manage/deploy/git.py +2 -2
- ominfra/manage/deploy/inject.py +2 -2
- ominfra/manage/deploy/tmp.py +9 -9
- ominfra/manage/deploy/venvs.py +2 -2
- ominfra/manage/targets/connection.py +1 -1
- ominfra/scripts/manage.py +208 -211
- {ominfra-0.0.0.dev160.dist-info → ominfra-0.0.0.dev161.dist-info}/METADATA +3 -3
- {ominfra-0.0.0.dev160.dist-info → ominfra-0.0.0.dev161.dist-info}/RECORD +12 -13
- ominfra/manage/deploy/atomics.py +0 -207
- {ominfra-0.0.0.dev160.dist-info → ominfra-0.0.0.dev161.dist-info}/LICENSE +0 -0
- {ominfra-0.0.0.dev160.dist-info → ominfra-0.0.0.dev161.dist-info}/WHEEL +0 -0
- {ominfra-0.0.0.dev160.dist-info → ominfra-0.0.0.dev161.dist-info}/entry_points.txt +0 -0
- {ominfra-0.0.0.dev160.dist-info → ominfra-0.0.0.dev161.dist-info}/top_level.txt +0 -0
ominfra/manage/deploy/git.py
CHANGED
@@ -15,8 +15,8 @@ import typing as ta
|
|
15
15
|
from omlish.asyncs.asyncio.subprocesses import asyncio_subprocesses
|
16
16
|
from omlish.lite.cached import async_cached_nullary
|
17
17
|
from omlish.lite.check import check
|
18
|
+
from omlish.os.atomics import AtomicPathSwapping
|
18
19
|
|
19
|
-
from .atomics import DeployAtomicPathSwapping
|
20
20
|
from .paths import SingleDirDeployPathOwner
|
21
21
|
from .specs import DeployGitCheckout
|
22
22
|
from .specs import DeployGitRepo
|
@@ -32,7 +32,7 @@ class DeployGitManager(SingleDirDeployPathOwner):
|
|
32
32
|
self,
|
33
33
|
*,
|
34
34
|
deploy_home: ta.Optional[DeployHome] = None,
|
35
|
-
atomics:
|
35
|
+
atomics: AtomicPathSwapping,
|
36
36
|
) -> None:
|
37
37
|
super().__init__(
|
38
38
|
owned_dir='git',
|
ominfra/manage/deploy/inject.py
CHANGED
@@ -5,10 +5,10 @@ import typing as ta
|
|
5
5
|
from omlish.lite.inject import InjectorBindingOrBindings
|
6
6
|
from omlish.lite.inject import InjectorBindings
|
7
7
|
from omlish.lite.inject import inj
|
8
|
+
from omlish.os.atomics import AtomicPathSwapping
|
8
9
|
|
9
10
|
from ..commands.inject import bind_command
|
10
11
|
from .apps import DeployAppManager
|
11
|
-
from .atomics import DeployAtomicPathSwapping
|
12
12
|
from .commands import DeployCommand
|
13
13
|
from .commands import DeployCommandExecutor
|
14
14
|
from .config import DeployConfig
|
@@ -34,7 +34,7 @@ def bind_deploy(
|
|
34
34
|
inj.bind(DeployGitManager, singleton=True),
|
35
35
|
|
36
36
|
inj.bind(DeployTmpManager, singleton=True),
|
37
|
-
inj.bind(
|
37
|
+
inj.bind(AtomicPathSwapping, to_key=DeployTmpManager),
|
38
38
|
|
39
39
|
inj.bind(DeployVenvManager, singleton=True),
|
40
40
|
|
ominfra/manage/deploy/tmp.py
CHANGED
@@ -3,18 +3,18 @@ import typing as ta
|
|
3
3
|
|
4
4
|
from omlish.lite.cached import cached_nullary
|
5
5
|
from omlish.lite.check import check
|
6
|
+
from omlish.os.atomics import AtomicPathSwap
|
7
|
+
from omlish.os.atomics import AtomicPathSwapKind
|
8
|
+
from omlish.os.atomics import AtomicPathSwapping
|
9
|
+
from omlish.os.atomics import TempDirAtomicPathSwapping
|
6
10
|
|
7
|
-
from .atomics import DeployAtomicPathSwap
|
8
|
-
from .atomics import DeployAtomicPathSwapKind
|
9
|
-
from .atomics import DeployAtomicPathSwapping
|
10
|
-
from .atomics import TempDirDeployAtomicPathSwapping
|
11
11
|
from .paths import SingleDirDeployPathOwner
|
12
12
|
from .types import DeployHome
|
13
13
|
|
14
14
|
|
15
15
|
class DeployTmpManager(
|
16
16
|
SingleDirDeployPathOwner,
|
17
|
-
|
17
|
+
AtomicPathSwapping,
|
18
18
|
):
|
19
19
|
def __init__(
|
20
20
|
self,
|
@@ -27,18 +27,18 @@ class DeployTmpManager(
|
|
27
27
|
)
|
28
28
|
|
29
29
|
@cached_nullary
|
30
|
-
def _swapping(self) ->
|
31
|
-
return
|
30
|
+
def _swapping(self) -> AtomicPathSwapping:
|
31
|
+
return TempDirAtomicPathSwapping(
|
32
32
|
temp_dir=self._make_dir(),
|
33
33
|
root_dir=check.non_empty_str(self._deploy_home),
|
34
34
|
)
|
35
35
|
|
36
36
|
def begin_atomic_path_swap(
|
37
37
|
self,
|
38
|
-
kind:
|
38
|
+
kind: AtomicPathSwapKind,
|
39
39
|
dst_path: str,
|
40
40
|
**kwargs: ta.Any,
|
41
|
-
) ->
|
41
|
+
) -> AtomicPathSwap:
|
42
42
|
return self._swapping().begin_atomic_path_swap(
|
43
43
|
kind,
|
44
44
|
dst_path,
|
ominfra/manage/deploy/venvs.py
CHANGED
@@ -10,8 +10,8 @@ import typing as ta
|
|
10
10
|
from omlish.asyncs.asyncio.subprocesses import asyncio_subprocesses
|
11
11
|
from omlish.lite.cached import cached_nullary
|
12
12
|
from omlish.lite.check import check
|
13
|
+
from omlish.os.atomics import AtomicPathSwapping
|
13
14
|
|
14
|
-
from .atomics import DeployAtomicPathSwapping
|
15
15
|
from .paths import DeployPath
|
16
16
|
from .paths import DeployPathOwner
|
17
17
|
from .types import DeployAppTag
|
@@ -23,7 +23,7 @@ class DeployVenvManager(DeployPathOwner):
|
|
23
23
|
self,
|
24
24
|
*,
|
25
25
|
deploy_home: ta.Optional[DeployHome] = None,
|
26
|
-
atomics:
|
26
|
+
atomics: AtomicPathSwapping,
|
27
27
|
) -> None:
|
28
28
|
super().__init__()
|
29
29
|
|
@@ -105,7 +105,7 @@ class DockerManageTargetConnector(ManageTargetConnector):
|
|
105
105
|
if dmt.image is not None:
|
106
106
|
sh_parts.extend(['run', '-i', dmt.image])
|
107
107
|
elif dmt.container_id is not None:
|
108
|
-
sh_parts.extend(['exec', dmt.container_id])
|
108
|
+
sh_parts.extend(['exec', '-i', dmt.container_id])
|
109
109
|
else:
|
110
110
|
raise ValueError(dmt)
|
111
111
|
|
ominfra/scripts/manage.py
CHANGED
@@ -100,10 +100,6 @@ CallableVersionOperator = ta.Callable[['Version', str], bool]
|
|
100
100
|
CommandT = ta.TypeVar('CommandT', bound='Command')
|
101
101
|
CommandOutputT = ta.TypeVar('CommandOutputT', bound='Command.Output')
|
102
102
|
|
103
|
-
# deploy/atomics.py
|
104
|
-
DeployAtomicPathSwapKind = ta.Literal['dir', 'file']
|
105
|
-
DeployAtomicPathSwapState = ta.Literal['open', 'committed', 'aborted'] # ta.TypeAlias
|
106
|
-
|
107
103
|
# deploy/paths.py
|
108
104
|
DeployPathKind = ta.Literal['dir', 'file'] # ta.TypeAlias
|
109
105
|
DeployPathPlaceholder = ta.Literal['app', 'tag'] # ta.TypeAlias
|
@@ -121,6 +117,10 @@ InjectorProviderFn = ta.Callable[['Injector'], ta.Any]
|
|
121
117
|
InjectorProviderFnMap = ta.Mapping['InjectorKey', 'InjectorProviderFn']
|
122
118
|
InjectorBindingOrBindings = ta.Union['InjectorBinding', 'InjectorBindings']
|
123
119
|
|
120
|
+
# ../../omlish/os/atomics.py
|
121
|
+
AtomicPathSwapKind = ta.Literal['dir', 'file']
|
122
|
+
AtomicPathSwapState = ta.Literal['open', 'committed', 'aborted'] # ta.TypeAlias
|
123
|
+
|
124
124
|
# ../configs.py
|
125
125
|
ConfigMapping = ta.Mapping[str, ta.Any]
|
126
126
|
|
@@ -4064,204 +4064,6 @@ def build_command_name_map(crs: CommandRegistrations) -> CommandNameMap:
|
|
4064
4064
|
return CommandNameMap(dct)
|
4065
4065
|
|
4066
4066
|
|
4067
|
-
########################################
|
4068
|
-
# ../deploy/atomics.py
|
4069
|
-
|
4070
|
-
|
4071
|
-
##
|
4072
|
-
|
4073
|
-
|
4074
|
-
class DeployAtomicPathSwap(abc.ABC):
|
4075
|
-
def __init__(
|
4076
|
-
self,
|
4077
|
-
kind: DeployAtomicPathSwapKind,
|
4078
|
-
dst_path: str,
|
4079
|
-
*,
|
4080
|
-
auto_commit: bool = False,
|
4081
|
-
) -> None:
|
4082
|
-
super().__init__()
|
4083
|
-
|
4084
|
-
self._kind = kind
|
4085
|
-
self._dst_path = dst_path
|
4086
|
-
self._auto_commit = auto_commit
|
4087
|
-
|
4088
|
-
self._state: DeployAtomicPathSwapState = 'open'
|
4089
|
-
|
4090
|
-
def __repr__(self) -> str:
|
4091
|
-
return attr_repr(self, 'kind', 'dst_path', 'tmp_path')
|
4092
|
-
|
4093
|
-
@property
|
4094
|
-
def kind(self) -> DeployAtomicPathSwapKind:
|
4095
|
-
return self._kind
|
4096
|
-
|
4097
|
-
@property
|
4098
|
-
def dst_path(self) -> str:
|
4099
|
-
return self._dst_path
|
4100
|
-
|
4101
|
-
@property
|
4102
|
-
@abc.abstractmethod
|
4103
|
-
def tmp_path(self) -> str:
|
4104
|
-
raise NotImplementedError
|
4105
|
-
|
4106
|
-
#
|
4107
|
-
|
4108
|
-
@property
|
4109
|
-
def state(self) -> DeployAtomicPathSwapState:
|
4110
|
-
return self._state
|
4111
|
-
|
4112
|
-
def _check_state(self, *states: DeployAtomicPathSwapState) -> None:
|
4113
|
-
if self._state not in states:
|
4114
|
-
raise RuntimeError(f'Atomic path swap not in correct state: {self._state}, {states}')
|
4115
|
-
|
4116
|
-
#
|
4117
|
-
|
4118
|
-
@abc.abstractmethod
|
4119
|
-
def _commit(self) -> None:
|
4120
|
-
raise NotImplementedError
|
4121
|
-
|
4122
|
-
def commit(self) -> None:
|
4123
|
-
if self._state == 'committed':
|
4124
|
-
return
|
4125
|
-
self._check_state('open')
|
4126
|
-
try:
|
4127
|
-
self._commit()
|
4128
|
-
except Exception: # noqa
|
4129
|
-
self._abort()
|
4130
|
-
raise
|
4131
|
-
else:
|
4132
|
-
self._state = 'committed'
|
4133
|
-
|
4134
|
-
#
|
4135
|
-
|
4136
|
-
@abc.abstractmethod
|
4137
|
-
def _abort(self) -> None:
|
4138
|
-
raise NotImplementedError
|
4139
|
-
|
4140
|
-
def abort(self) -> None:
|
4141
|
-
if self._state == 'aborted':
|
4142
|
-
return
|
4143
|
-
self._abort()
|
4144
|
-
self._state = 'aborted'
|
4145
|
-
|
4146
|
-
#
|
4147
|
-
|
4148
|
-
def __enter__(self) -> 'DeployAtomicPathSwap':
|
4149
|
-
return self
|
4150
|
-
|
4151
|
-
def __exit__(self, exc_type, exc_val, exc_tb):
|
4152
|
-
if (
|
4153
|
-
exc_type is None and
|
4154
|
-
self._auto_commit and
|
4155
|
-
self._state == 'open'
|
4156
|
-
):
|
4157
|
-
self.commit()
|
4158
|
-
else:
|
4159
|
-
self.abort()
|
4160
|
-
|
4161
|
-
|
4162
|
-
#
|
4163
|
-
|
4164
|
-
|
4165
|
-
class DeployAtomicPathSwapping(abc.ABC):
|
4166
|
-
@abc.abstractmethod
|
4167
|
-
def begin_atomic_path_swap(
|
4168
|
-
self,
|
4169
|
-
kind: DeployAtomicPathSwapKind,
|
4170
|
-
dst_path: str,
|
4171
|
-
*,
|
4172
|
-
name_hint: ta.Optional[str] = None,
|
4173
|
-
make_dirs: bool = False,
|
4174
|
-
**kwargs: ta.Any,
|
4175
|
-
) -> DeployAtomicPathSwap:
|
4176
|
-
raise NotImplementedError
|
4177
|
-
|
4178
|
-
|
4179
|
-
##
|
4180
|
-
|
4181
|
-
|
4182
|
-
class OsRenameDeployAtomicPathSwap(DeployAtomicPathSwap):
|
4183
|
-
def __init__(
|
4184
|
-
self,
|
4185
|
-
kind: DeployAtomicPathSwapKind,
|
4186
|
-
dst_path: str,
|
4187
|
-
tmp_path: str,
|
4188
|
-
**kwargs: ta.Any,
|
4189
|
-
) -> None:
|
4190
|
-
if kind == 'dir':
|
4191
|
-
check.state(os.path.isdir(tmp_path))
|
4192
|
-
elif kind == 'file':
|
4193
|
-
check.state(os.path.isfile(tmp_path))
|
4194
|
-
else:
|
4195
|
-
raise TypeError(kind)
|
4196
|
-
|
4197
|
-
super().__init__(
|
4198
|
-
kind,
|
4199
|
-
dst_path,
|
4200
|
-
**kwargs,
|
4201
|
-
)
|
4202
|
-
|
4203
|
-
self._tmp_path = tmp_path
|
4204
|
-
|
4205
|
-
@property
|
4206
|
-
def tmp_path(self) -> str:
|
4207
|
-
return self._tmp_path
|
4208
|
-
|
4209
|
-
def _commit(self) -> None:
|
4210
|
-
os.rename(self._tmp_path, self._dst_path)
|
4211
|
-
|
4212
|
-
def _abort(self) -> None:
|
4213
|
-
shutil.rmtree(self._tmp_path, ignore_errors=True)
|
4214
|
-
|
4215
|
-
|
4216
|
-
class TempDirDeployAtomicPathSwapping(DeployAtomicPathSwapping):
|
4217
|
-
def __init__(
|
4218
|
-
self,
|
4219
|
-
*,
|
4220
|
-
temp_dir: ta.Optional[str] = None,
|
4221
|
-
root_dir: ta.Optional[str] = None,
|
4222
|
-
) -> None:
|
4223
|
-
super().__init__()
|
4224
|
-
|
4225
|
-
if root_dir is not None:
|
4226
|
-
root_dir = os.path.abspath(root_dir)
|
4227
|
-
self._root_dir = root_dir
|
4228
|
-
self._temp_dir = temp_dir
|
4229
|
-
|
4230
|
-
def begin_atomic_path_swap(
|
4231
|
-
self,
|
4232
|
-
kind: DeployAtomicPathSwapKind,
|
4233
|
-
dst_path: str,
|
4234
|
-
*,
|
4235
|
-
name_hint: ta.Optional[str] = None,
|
4236
|
-
make_dirs: bool = False,
|
4237
|
-
**kwargs: ta.Any,
|
4238
|
-
) -> DeployAtomicPathSwap:
|
4239
|
-
dst_path = os.path.abspath(dst_path)
|
4240
|
-
if self._root_dir is not None and not dst_path.startswith(check.non_empty_str(self._root_dir)):
|
4241
|
-
raise RuntimeError(f'Atomic path swap dst must be in root dir: {dst_path}, {self._root_dir}')
|
4242
|
-
|
4243
|
-
dst_dir = os.path.dirname(dst_path)
|
4244
|
-
if make_dirs:
|
4245
|
-
os.makedirs(dst_dir, exist_ok=True)
|
4246
|
-
if not os.path.isdir(dst_dir):
|
4247
|
-
raise RuntimeError(f'Atomic path swap dst dir does not exist: {dst_dir}')
|
4248
|
-
|
4249
|
-
if kind == 'dir':
|
4250
|
-
tmp_path = tempfile.mkdtemp(prefix=name_hint, dir=self._temp_dir)
|
4251
|
-
elif kind == 'file':
|
4252
|
-
fd, tmp_path = tempfile.mkstemp(prefix=name_hint, dir=self._temp_dir)
|
4253
|
-
os.close(fd)
|
4254
|
-
else:
|
4255
|
-
raise TypeError(kind)
|
4256
|
-
|
4257
|
-
return OsRenameDeployAtomicPathSwap(
|
4258
|
-
kind,
|
4259
|
-
dst_path,
|
4260
|
-
tmp_path,
|
4261
|
-
**kwargs,
|
4262
|
-
)
|
4263
|
-
|
4264
|
-
|
4265
4067
|
########################################
|
4266
4068
|
# ../deploy/paths.py
|
4267
4069
|
"""
|
@@ -6516,6 +6318,201 @@ class JsonLogFormatter(logging.Formatter):
|
|
6516
6318
|
return self._json_dumps(dct)
|
6517
6319
|
|
6518
6320
|
|
6321
|
+
########################################
|
6322
|
+
# ../../../omlish/os/atomics.py
|
6323
|
+
|
6324
|
+
|
6325
|
+
##
|
6326
|
+
|
6327
|
+
|
6328
|
+
class AtomicPathSwap(abc.ABC):
|
6329
|
+
def __init__(
|
6330
|
+
self,
|
6331
|
+
kind: AtomicPathSwapKind,
|
6332
|
+
dst_path: str,
|
6333
|
+
*,
|
6334
|
+
auto_commit: bool = False,
|
6335
|
+
) -> None:
|
6336
|
+
super().__init__()
|
6337
|
+
|
6338
|
+
self._kind = kind
|
6339
|
+
self._dst_path = dst_path
|
6340
|
+
self._auto_commit = auto_commit
|
6341
|
+
|
6342
|
+
self._state: AtomicPathSwapState = 'open'
|
6343
|
+
|
6344
|
+
def __repr__(self) -> str:
|
6345
|
+
return attr_repr(self, 'kind', 'dst_path', 'tmp_path')
|
6346
|
+
|
6347
|
+
@property
|
6348
|
+
def kind(self) -> AtomicPathSwapKind:
|
6349
|
+
return self._kind
|
6350
|
+
|
6351
|
+
@property
|
6352
|
+
def dst_path(self) -> str:
|
6353
|
+
return self._dst_path
|
6354
|
+
|
6355
|
+
@property
|
6356
|
+
@abc.abstractmethod
|
6357
|
+
def tmp_path(self) -> str:
|
6358
|
+
raise NotImplementedError
|
6359
|
+
|
6360
|
+
#
|
6361
|
+
|
6362
|
+
@property
|
6363
|
+
def state(self) -> AtomicPathSwapState:
|
6364
|
+
return self._state
|
6365
|
+
|
6366
|
+
def _check_state(self, *states: AtomicPathSwapState) -> None:
|
6367
|
+
if self._state not in states:
|
6368
|
+
raise RuntimeError(f'Atomic path swap not in correct state: {self._state}, {states}')
|
6369
|
+
|
6370
|
+
#
|
6371
|
+
|
6372
|
+
@abc.abstractmethod
|
6373
|
+
def _commit(self) -> None:
|
6374
|
+
raise NotImplementedError
|
6375
|
+
|
6376
|
+
def commit(self) -> None:
|
6377
|
+
if self._state == 'committed':
|
6378
|
+
return
|
6379
|
+
self._check_state('open')
|
6380
|
+
try:
|
6381
|
+
self._commit()
|
6382
|
+
except Exception: # noqa
|
6383
|
+
self._abort()
|
6384
|
+
raise
|
6385
|
+
else:
|
6386
|
+
self._state = 'committed'
|
6387
|
+
|
6388
|
+
#
|
6389
|
+
|
6390
|
+
@abc.abstractmethod
|
6391
|
+
def _abort(self) -> None:
|
6392
|
+
raise NotImplementedError
|
6393
|
+
|
6394
|
+
def abort(self) -> None:
|
6395
|
+
if self._state == 'aborted':
|
6396
|
+
return
|
6397
|
+
self._abort()
|
6398
|
+
self._state = 'aborted'
|
6399
|
+
|
6400
|
+
#
|
6401
|
+
|
6402
|
+
def __enter__(self) -> 'AtomicPathSwap':
|
6403
|
+
return self
|
6404
|
+
|
6405
|
+
def __exit__(self, exc_type, exc_val, exc_tb):
|
6406
|
+
if (
|
6407
|
+
exc_type is None and
|
6408
|
+
self._auto_commit and
|
6409
|
+
self._state == 'open'
|
6410
|
+
):
|
6411
|
+
self.commit()
|
6412
|
+
else:
|
6413
|
+
self.abort()
|
6414
|
+
|
6415
|
+
|
6416
|
+
class AtomicPathSwapping(abc.ABC):
|
6417
|
+
@abc.abstractmethod
|
6418
|
+
def begin_atomic_path_swap(
|
6419
|
+
self,
|
6420
|
+
kind: AtomicPathSwapKind,
|
6421
|
+
dst_path: str,
|
6422
|
+
*,
|
6423
|
+
name_hint: ta.Optional[str] = None,
|
6424
|
+
make_dirs: bool = False,
|
6425
|
+
**kwargs: ta.Any,
|
6426
|
+
) -> AtomicPathSwap:
|
6427
|
+
raise NotImplementedError
|
6428
|
+
|
6429
|
+
|
6430
|
+
##
|
6431
|
+
|
6432
|
+
|
6433
|
+
class OsRenameAtomicPathSwap(AtomicPathSwap):
|
6434
|
+
def __init__(
|
6435
|
+
self,
|
6436
|
+
kind: AtomicPathSwapKind,
|
6437
|
+
dst_path: str,
|
6438
|
+
tmp_path: str,
|
6439
|
+
**kwargs: ta.Any,
|
6440
|
+
) -> None:
|
6441
|
+
if kind == 'dir':
|
6442
|
+
check.state(os.path.isdir(tmp_path))
|
6443
|
+
elif kind == 'file':
|
6444
|
+
check.state(os.path.isfile(tmp_path))
|
6445
|
+
else:
|
6446
|
+
raise TypeError(kind)
|
6447
|
+
|
6448
|
+
super().__init__(
|
6449
|
+
kind,
|
6450
|
+
dst_path,
|
6451
|
+
**kwargs,
|
6452
|
+
)
|
6453
|
+
|
6454
|
+
self._tmp_path = tmp_path
|
6455
|
+
|
6456
|
+
@property
|
6457
|
+
def tmp_path(self) -> str:
|
6458
|
+
return self._tmp_path
|
6459
|
+
|
6460
|
+
def _commit(self) -> None:
|
6461
|
+
os.rename(self._tmp_path, self._dst_path)
|
6462
|
+
|
6463
|
+
def _abort(self) -> None:
|
6464
|
+
shutil.rmtree(self._tmp_path, ignore_errors=True)
|
6465
|
+
|
6466
|
+
|
6467
|
+
class TempDirAtomicPathSwapping(AtomicPathSwapping):
|
6468
|
+
def __init__(
|
6469
|
+
self,
|
6470
|
+
*,
|
6471
|
+
temp_dir: ta.Optional[str] = None,
|
6472
|
+
root_dir: ta.Optional[str] = None,
|
6473
|
+
) -> None:
|
6474
|
+
super().__init__()
|
6475
|
+
|
6476
|
+
if root_dir is not None:
|
6477
|
+
root_dir = os.path.abspath(root_dir)
|
6478
|
+
self._root_dir = root_dir
|
6479
|
+
self._temp_dir = temp_dir
|
6480
|
+
|
6481
|
+
def begin_atomic_path_swap(
|
6482
|
+
self,
|
6483
|
+
kind: AtomicPathSwapKind,
|
6484
|
+
dst_path: str,
|
6485
|
+
*,
|
6486
|
+
name_hint: ta.Optional[str] = None,
|
6487
|
+
make_dirs: bool = False,
|
6488
|
+
**kwargs: ta.Any,
|
6489
|
+
) -> AtomicPathSwap:
|
6490
|
+
dst_path = os.path.abspath(dst_path)
|
6491
|
+
if self._root_dir is not None and not dst_path.startswith(check.non_empty_str(self._root_dir)):
|
6492
|
+
raise RuntimeError(f'Atomic path swap dst must be in root dir: {dst_path}, {self._root_dir}')
|
6493
|
+
|
6494
|
+
dst_dir = os.path.dirname(dst_path)
|
6495
|
+
if make_dirs:
|
6496
|
+
os.makedirs(dst_dir, exist_ok=True)
|
6497
|
+
if not os.path.isdir(dst_dir):
|
6498
|
+
raise RuntimeError(f'Atomic path swap dst dir does not exist: {dst_dir}')
|
6499
|
+
|
6500
|
+
if kind == 'dir':
|
6501
|
+
tmp_path = tempfile.mkdtemp(prefix=name_hint, dir=self._temp_dir)
|
6502
|
+
elif kind == 'file':
|
6503
|
+
fd, tmp_path = tempfile.mkstemp(prefix=name_hint, dir=self._temp_dir)
|
6504
|
+
os.close(fd)
|
6505
|
+
else:
|
6506
|
+
raise TypeError(kind)
|
6507
|
+
|
6508
|
+
return OsRenameAtomicPathSwap(
|
6509
|
+
kind,
|
6510
|
+
dst_path,
|
6511
|
+
tmp_path,
|
6512
|
+
**kwargs,
|
6513
|
+
)
|
6514
|
+
|
6515
|
+
|
6519
6516
|
########################################
|
6520
6517
|
# ../../../omdev/interp/types.py
|
6521
6518
|
|
@@ -6752,7 +6749,7 @@ CommandExecutorMap = ta.NewType('CommandExecutorMap', ta.Mapping[ta.Type[Command
|
|
6752
6749
|
|
6753
6750
|
class DeployTmpManager(
|
6754
6751
|
SingleDirDeployPathOwner,
|
6755
|
-
|
6752
|
+
AtomicPathSwapping,
|
6756
6753
|
):
|
6757
6754
|
def __init__(
|
6758
6755
|
self,
|
@@ -6765,18 +6762,18 @@ class DeployTmpManager(
|
|
6765
6762
|
)
|
6766
6763
|
|
6767
6764
|
@cached_nullary
|
6768
|
-
def _swapping(self) ->
|
6769
|
-
return
|
6765
|
+
def _swapping(self) -> AtomicPathSwapping:
|
6766
|
+
return TempDirAtomicPathSwapping(
|
6770
6767
|
temp_dir=self._make_dir(),
|
6771
6768
|
root_dir=check.non_empty_str(self._deploy_home),
|
6772
6769
|
)
|
6773
6770
|
|
6774
6771
|
def begin_atomic_path_swap(
|
6775
6772
|
self,
|
6776
|
-
kind:
|
6773
|
+
kind: AtomicPathSwapKind,
|
6777
6774
|
dst_path: str,
|
6778
6775
|
**kwargs: ta.Any,
|
6779
|
-
) ->
|
6776
|
+
) -> AtomicPathSwap:
|
6780
6777
|
return self._swapping().begin_atomic_path_swap(
|
6781
6778
|
kind,
|
6782
6779
|
dst_path,
|
@@ -8175,7 +8172,7 @@ class DeployGitManager(SingleDirDeployPathOwner):
|
|
8175
8172
|
self,
|
8176
8173
|
*,
|
8177
8174
|
deploy_home: ta.Optional[DeployHome] = None,
|
8178
|
-
atomics:
|
8175
|
+
atomics: AtomicPathSwapping,
|
8179
8176
|
) -> None:
|
8180
8177
|
super().__init__(
|
8181
8178
|
owned_dir='git',
|
@@ -8280,7 +8277,7 @@ class DeployVenvManager(DeployPathOwner):
|
|
8280
8277
|
self,
|
8281
8278
|
*,
|
8282
8279
|
deploy_home: ta.Optional[DeployHome] = None,
|
8283
|
-
atomics:
|
8280
|
+
atomics: AtomicPathSwapping,
|
8284
8281
|
) -> None:
|
8285
8282
|
super().__init__()
|
8286
8283
|
|
@@ -9824,7 +9821,7 @@ class DockerManageTargetConnector(ManageTargetConnector):
|
|
9824
9821
|
if dmt.image is not None:
|
9825
9822
|
sh_parts.extend(['run', '-i', dmt.image])
|
9826
9823
|
elif dmt.container_id is not None:
|
9827
|
-
sh_parts.extend(['exec', dmt.container_id])
|
9824
|
+
sh_parts.extend(['exec', '-i', dmt.container_id])
|
9828
9825
|
else:
|
9829
9826
|
raise ValueError(dmt)
|
9830
9827
|
|
@@ -10044,7 +10041,7 @@ def bind_deploy(
|
|
10044
10041
|
inj.bind(DeployGitManager, singleton=True),
|
10045
10042
|
|
10046
10043
|
inj.bind(DeployTmpManager, singleton=True),
|
10047
|
-
inj.bind(
|
10044
|
+
inj.bind(AtomicPathSwapping, to_key=DeployTmpManager),
|
10048
10045
|
|
10049
10046
|
inj.bind(DeployVenvManager, singleton=True),
|
10050
10047
|
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: ominfra
|
3
|
-
Version: 0.0.0.
|
3
|
+
Version: 0.0.0.dev161
|
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.dev161
|
16
|
+
Requires-Dist: omlish==0.0.0.dev161
|
17
17
|
Provides-Extra: all
|
18
18
|
Requires-Dist: paramiko~=3.5; extra == "all"
|
19
19
|
Requires-Dist: asyncssh~=2.18; extra == "all"
|
@@ -45,17 +45,16 @@ ominfra/manage/commands/subprocess.py,sha256=yHGMbAI-xKe_9BUs5IZ3Yav8qRE-I9aGnBt
|
|
45
45
|
ominfra/manage/commands/types.py,sha256=XFZPeqeIBAaIIQF3pdPbGxLlb-LCrz6WtlDWO2q_vz0,210
|
46
46
|
ominfra/manage/deploy/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
47
47
|
ominfra/manage/deploy/apps.py,sha256=lXcbyX8_wrvvwKtIMM9P_Mh7xL8yj6z9_PFTl_0u-0U,1887
|
48
|
-
ominfra/manage/deploy/atomics.py,sha256=j9_L2LXls2dR1I4rQw3msIa2D90JwEO9Mb8KBGOKmyU,5180
|
49
48
|
ominfra/manage/deploy/commands.py,sha256=N9qVntnRgJ_IneI7rEQB2Za0oU7gouPfm-sl2MCwW1E,764
|
50
49
|
ominfra/manage/deploy/config.py,sha256=aR6ubMEWqkTI55XtcG1Cczn6YhCVN6eSL8DT5EHQJN0,166
|
51
|
-
ominfra/manage/deploy/git.py,sha256=
|
52
|
-
ominfra/manage/deploy/inject.py,sha256=
|
50
|
+
ominfra/manage/deploy/git.py,sha256=6CGLvGH8uYkuT8gyZHybJb7sgUPTtFgy7grj1YHkI9g,3747
|
51
|
+
ominfra/manage/deploy/inject.py,sha256=8wuIgdzkDCHbc69nD1meLjwfCOMdWOIfDT5yijL-du8,1441
|
53
52
|
ominfra/manage/deploy/interp.py,sha256=OKkenH8YKEW_mEDR6X7_ZLxK9a1Ox6KHSwFPTHT6OzA,1029
|
54
53
|
ominfra/manage/deploy/paths.py,sha256=tK8zZFWOHDRdTN5AlTe-3MpgZqovhWrljGosQmeEYvo,6839
|
55
54
|
ominfra/manage/deploy/specs.py,sha256=Yq3WiLNJcodUBEsJfP18gPGB3X2ABI1g8YLlsUvJOXg,1230
|
56
|
-
ominfra/manage/deploy/tmp.py,sha256=
|
55
|
+
ominfra/manage/deploy/tmp.py,sha256=L0pIfQuxQ7_6gC_AAv7eubI37_IPzCVR29hkn1MHL2Q,1230
|
57
56
|
ominfra/manage/deploy/types.py,sha256=o95wqvTGNRq8Cxx7VpqeX-9x1tI8k8BpqPFvJZkJYBA,305
|
58
|
-
ominfra/manage/deploy/venvs.py,sha256=
|
57
|
+
ominfra/manage/deploy/venvs.py,sha256=ZG9tXhzg0R173T1IFIc5DE45xEuJK97DjlN1cQPAvXI,2229
|
59
58
|
ominfra/manage/remote/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
60
59
|
ominfra/manage/remote/_main.py,sha256=p5KoiS2WMw6QAqlDl_Zun-JybmCsy8awIfpBMLBjGMY,4356
|
61
60
|
ominfra/manage/remote/channel.py,sha256=36xR9Ti9ZA8TUBtxmY0u7_3Lv7E6wzQTxlZl7gLR5GE,2224
|
@@ -72,12 +71,12 @@ ominfra/manage/system/inject.py,sha256=Ksc7Xw_Yh3lWwkTRxB2JCeOYO-nMyKj-Kssd1RDkR
|
|
72
71
|
ominfra/manage/system/packages.py,sha256=HImCsgzXxcfzAoD2PjrstaSE_qgBWtTFUVb0lTEOxkI,4614
|
73
72
|
ominfra/manage/system/platforms.py,sha256=F0bgYzUzCjZ2LbWVvnEq2ya_X_LfRW406LQYFL7bG44,1202
|
74
73
|
ominfra/manage/targets/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
75
|
-
ominfra/manage/targets/connection.py,sha256=
|
74
|
+
ominfra/manage/targets/connection.py,sha256=5e8h9Miej2DKJxZfLyxpGe8y-Y0V_b_AuUW159XVeJM,4643
|
76
75
|
ominfra/manage/targets/inject.py,sha256=P4597xWM-V3I_gCt2O71OLhYQkkXtuJvkYRsIbhhMcE,1561
|
77
76
|
ominfra/manage/targets/targets.py,sha256=CFl8Uirgn3gfowO1Fn-LBK-6qYqEMFJ9snPUl0gCRuM,1753
|
78
77
|
ominfra/scripts/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
79
78
|
ominfra/scripts/journald2aws.py,sha256=EC8tSKW3hztBV_Kr_ykK72AmcvnWivUxcz6Sfg3M_hI,155085
|
80
|
-
ominfra/scripts/manage.py,sha256=
|
79
|
+
ominfra/scripts/manage.py,sha256=MQQGcgbIDwUfD31nDGJD2DhWhlazj8GVwQi7kOzi3gc,293788
|
81
80
|
ominfra/scripts/supervisor.py,sha256=npGYEWSZfY7E24mdkJ3HrL_ax6AcqjHfqh-7nZ_sX0U,273987
|
82
81
|
ominfra/supervisor/LICENSE.txt,sha256=yvqaMNsDhWxziHa9ien6qCW1SkZv-DQlAg96XjfSee8,1746
|
83
82
|
ominfra/supervisor/__init__.py,sha256=Y3l4WY4JRi2uLG6kgbGp93fuGfkxkKwZDvhsa0Rwgtk,15
|
@@ -120,9 +119,9 @@ ominfra/tailscale/api.py,sha256=C5-t_b6jZXUWcy5k8bXm7CFnk73pSdrlMOgGDeGVrpw,1370
|
|
120
119
|
ominfra/tailscale/cli.py,sha256=h6akQJMl0KuWLHS7Ur6WcBZ2JwF0DJQhsPTnFBdGyNk,3571
|
121
120
|
ominfra/tools/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
122
121
|
ominfra/tools/listresources.py,sha256=4qVg5txsb10EHhvqXXeM6gJ2jx9LbroEnPydDv1uXs0,6176
|
123
|
-
ominfra-0.0.0.
|
124
|
-
ominfra-0.0.0.
|
125
|
-
ominfra-0.0.0.
|
126
|
-
ominfra-0.0.0.
|
127
|
-
ominfra-0.0.0.
|
128
|
-
ominfra-0.0.0.
|
122
|
+
ominfra-0.0.0.dev161.dist-info/LICENSE,sha256=B_hVtavaA8zCYDW99DYdcpDLKz1n3BBRjZrcbv8uG8c,1451
|
123
|
+
ominfra-0.0.0.dev161.dist-info/METADATA,sha256=9BUyOL7-nhzomsloRZDx7T8VL-T0KbCHxyQ74LxzbFY,731
|
124
|
+
ominfra-0.0.0.dev161.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91
|
125
|
+
ominfra-0.0.0.dev161.dist-info/entry_points.txt,sha256=kgecQ2MgGrM9qK744BoKS3tMesaC3yjLnl9pa5CRczg,37
|
126
|
+
ominfra-0.0.0.dev161.dist-info/top_level.txt,sha256=E-b2OHkk_AOBLXHYZQ2EOFKl-_6uOGd8EjeG-Zy6h_w,8
|
127
|
+
ominfra-0.0.0.dev161.dist-info/RECORD,,
|
ominfra/manage/deploy/atomics.py
DELETED
@@ -1,207 +0,0 @@
|
|
1
|
-
# ruff: noqa: UP006 UP007
|
2
|
-
import abc
|
3
|
-
import os
|
4
|
-
import shutil
|
5
|
-
import tempfile
|
6
|
-
import typing as ta
|
7
|
-
|
8
|
-
from omlish.lite.check import check
|
9
|
-
from omlish.lite.strings import attr_repr
|
10
|
-
|
11
|
-
|
12
|
-
DeployAtomicPathSwapKind = ta.Literal['dir', 'file']
|
13
|
-
DeployAtomicPathSwapState = ta.Literal['open', 'committed', 'aborted'] # ta.TypeAlias
|
14
|
-
|
15
|
-
|
16
|
-
##
|
17
|
-
|
18
|
-
|
19
|
-
class DeployAtomicPathSwap(abc.ABC):
|
20
|
-
def __init__(
|
21
|
-
self,
|
22
|
-
kind: DeployAtomicPathSwapKind,
|
23
|
-
dst_path: str,
|
24
|
-
*,
|
25
|
-
auto_commit: bool = False,
|
26
|
-
) -> None:
|
27
|
-
super().__init__()
|
28
|
-
|
29
|
-
self._kind = kind
|
30
|
-
self._dst_path = dst_path
|
31
|
-
self._auto_commit = auto_commit
|
32
|
-
|
33
|
-
self._state: DeployAtomicPathSwapState = 'open'
|
34
|
-
|
35
|
-
def __repr__(self) -> str:
|
36
|
-
return attr_repr(self, 'kind', 'dst_path', 'tmp_path')
|
37
|
-
|
38
|
-
@property
|
39
|
-
def kind(self) -> DeployAtomicPathSwapKind:
|
40
|
-
return self._kind
|
41
|
-
|
42
|
-
@property
|
43
|
-
def dst_path(self) -> str:
|
44
|
-
return self._dst_path
|
45
|
-
|
46
|
-
@property
|
47
|
-
@abc.abstractmethod
|
48
|
-
def tmp_path(self) -> str:
|
49
|
-
raise NotImplementedError
|
50
|
-
|
51
|
-
#
|
52
|
-
|
53
|
-
@property
|
54
|
-
def state(self) -> DeployAtomicPathSwapState:
|
55
|
-
return self._state
|
56
|
-
|
57
|
-
def _check_state(self, *states: DeployAtomicPathSwapState) -> None:
|
58
|
-
if self._state not in states:
|
59
|
-
raise RuntimeError(f'Atomic path swap not in correct state: {self._state}, {states}')
|
60
|
-
|
61
|
-
#
|
62
|
-
|
63
|
-
@abc.abstractmethod
|
64
|
-
def _commit(self) -> None:
|
65
|
-
raise NotImplementedError
|
66
|
-
|
67
|
-
def commit(self) -> None:
|
68
|
-
if self._state == 'committed':
|
69
|
-
return
|
70
|
-
self._check_state('open')
|
71
|
-
try:
|
72
|
-
self._commit()
|
73
|
-
except Exception: # noqa
|
74
|
-
self._abort()
|
75
|
-
raise
|
76
|
-
else:
|
77
|
-
self._state = 'committed'
|
78
|
-
|
79
|
-
#
|
80
|
-
|
81
|
-
@abc.abstractmethod
|
82
|
-
def _abort(self) -> None:
|
83
|
-
raise NotImplementedError
|
84
|
-
|
85
|
-
def abort(self) -> None:
|
86
|
-
if self._state == 'aborted':
|
87
|
-
return
|
88
|
-
self._abort()
|
89
|
-
self._state = 'aborted'
|
90
|
-
|
91
|
-
#
|
92
|
-
|
93
|
-
def __enter__(self) -> 'DeployAtomicPathSwap':
|
94
|
-
return self
|
95
|
-
|
96
|
-
def __exit__(self, exc_type, exc_val, exc_tb):
|
97
|
-
if (
|
98
|
-
exc_type is None and
|
99
|
-
self._auto_commit and
|
100
|
-
self._state == 'open'
|
101
|
-
):
|
102
|
-
self.commit()
|
103
|
-
else:
|
104
|
-
self.abort()
|
105
|
-
|
106
|
-
|
107
|
-
#
|
108
|
-
|
109
|
-
|
110
|
-
class DeployAtomicPathSwapping(abc.ABC):
|
111
|
-
@abc.abstractmethod
|
112
|
-
def begin_atomic_path_swap(
|
113
|
-
self,
|
114
|
-
kind: DeployAtomicPathSwapKind,
|
115
|
-
dst_path: str,
|
116
|
-
*,
|
117
|
-
name_hint: ta.Optional[str] = None,
|
118
|
-
make_dirs: bool = False,
|
119
|
-
**kwargs: ta.Any,
|
120
|
-
) -> DeployAtomicPathSwap:
|
121
|
-
raise NotImplementedError
|
122
|
-
|
123
|
-
|
124
|
-
##
|
125
|
-
|
126
|
-
|
127
|
-
class OsRenameDeployAtomicPathSwap(DeployAtomicPathSwap):
|
128
|
-
def __init__(
|
129
|
-
self,
|
130
|
-
kind: DeployAtomicPathSwapKind,
|
131
|
-
dst_path: str,
|
132
|
-
tmp_path: str,
|
133
|
-
**kwargs: ta.Any,
|
134
|
-
) -> None:
|
135
|
-
if kind == 'dir':
|
136
|
-
check.state(os.path.isdir(tmp_path))
|
137
|
-
elif kind == 'file':
|
138
|
-
check.state(os.path.isfile(tmp_path))
|
139
|
-
else:
|
140
|
-
raise TypeError(kind)
|
141
|
-
|
142
|
-
super().__init__(
|
143
|
-
kind,
|
144
|
-
dst_path,
|
145
|
-
**kwargs,
|
146
|
-
)
|
147
|
-
|
148
|
-
self._tmp_path = tmp_path
|
149
|
-
|
150
|
-
@property
|
151
|
-
def tmp_path(self) -> str:
|
152
|
-
return self._tmp_path
|
153
|
-
|
154
|
-
def _commit(self) -> None:
|
155
|
-
os.rename(self._tmp_path, self._dst_path)
|
156
|
-
|
157
|
-
def _abort(self) -> None:
|
158
|
-
shutil.rmtree(self._tmp_path, ignore_errors=True)
|
159
|
-
|
160
|
-
|
161
|
-
class TempDirDeployAtomicPathSwapping(DeployAtomicPathSwapping):
|
162
|
-
def __init__(
|
163
|
-
self,
|
164
|
-
*,
|
165
|
-
temp_dir: ta.Optional[str] = None,
|
166
|
-
root_dir: ta.Optional[str] = None,
|
167
|
-
) -> None:
|
168
|
-
super().__init__()
|
169
|
-
|
170
|
-
if root_dir is not None:
|
171
|
-
root_dir = os.path.abspath(root_dir)
|
172
|
-
self._root_dir = root_dir
|
173
|
-
self._temp_dir = temp_dir
|
174
|
-
|
175
|
-
def begin_atomic_path_swap(
|
176
|
-
self,
|
177
|
-
kind: DeployAtomicPathSwapKind,
|
178
|
-
dst_path: str,
|
179
|
-
*,
|
180
|
-
name_hint: ta.Optional[str] = None,
|
181
|
-
make_dirs: bool = False,
|
182
|
-
**kwargs: ta.Any,
|
183
|
-
) -> DeployAtomicPathSwap:
|
184
|
-
dst_path = os.path.abspath(dst_path)
|
185
|
-
if self._root_dir is not None and not dst_path.startswith(check.non_empty_str(self._root_dir)):
|
186
|
-
raise RuntimeError(f'Atomic path swap dst must be in root dir: {dst_path}, {self._root_dir}')
|
187
|
-
|
188
|
-
dst_dir = os.path.dirname(dst_path)
|
189
|
-
if make_dirs:
|
190
|
-
os.makedirs(dst_dir, exist_ok=True)
|
191
|
-
if not os.path.isdir(dst_dir):
|
192
|
-
raise RuntimeError(f'Atomic path swap dst dir does not exist: {dst_dir}')
|
193
|
-
|
194
|
-
if kind == 'dir':
|
195
|
-
tmp_path = tempfile.mkdtemp(prefix=name_hint, dir=self._temp_dir)
|
196
|
-
elif kind == 'file':
|
197
|
-
fd, tmp_path = tempfile.mkstemp(prefix=name_hint, dir=self._temp_dir)
|
198
|
-
os.close(fd)
|
199
|
-
else:
|
200
|
-
raise TypeError(kind)
|
201
|
-
|
202
|
-
return OsRenameDeployAtomicPathSwap(
|
203
|
-
kind,
|
204
|
-
dst_path,
|
205
|
-
tmp_path,
|
206
|
-
**kwargs,
|
207
|
-
)
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|