ominfra 0.0.0.dev158__py3-none-any.whl → 0.0.0.dev159__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/apps.py +11 -8
- ominfra/manage/deploy/atomics.py +207 -0
- ominfra/manage/deploy/git.py +24 -28
- ominfra/manage/deploy/inject.py +11 -0
- ominfra/manage/deploy/paths.py +41 -3
- ominfra/manage/deploy/specs.py +10 -0
- ominfra/manage/deploy/tmp.py +46 -0
- ominfra/manage/deploy/types.py +1 -0
- ominfra/manage/deploy/venvs.py +6 -1
- ominfra/scripts/journald2aws.py +11 -9
- ominfra/scripts/manage.py +343 -44
- ominfra/scripts/supervisor.py +11 -9
- {ominfra-0.0.0.dev158.dist-info → ominfra-0.0.0.dev159.dist-info}/METADATA +3 -3
- {ominfra-0.0.0.dev158.dist-info → ominfra-0.0.0.dev159.dist-info}/RECORD +18 -16
- {ominfra-0.0.0.dev158.dist-info → ominfra-0.0.0.dev159.dist-info}/LICENSE +0 -0
- {ominfra-0.0.0.dev158.dist-info → ominfra-0.0.0.dev159.dist-info}/WHEEL +0 -0
- {ominfra-0.0.0.dev158.dist-info → ominfra-0.0.0.dev159.dist-info}/entry_points.txt +0 -0
- {ominfra-0.0.0.dev158.dist-info → ominfra-0.0.0.dev159.dist-info}/top_level.txt +0 -0
ominfra/scripts/manage.py
CHANGED
@@ -24,6 +24,7 @@ import decimal
|
|
24
24
|
import enum
|
25
25
|
import fractions
|
26
26
|
import functools
|
27
|
+
import hashlib
|
27
28
|
import inspect
|
28
29
|
import itertools
|
29
30
|
import json
|
@@ -41,6 +42,7 @@ import string
|
|
41
42
|
import struct
|
42
43
|
import subprocess
|
43
44
|
import sys
|
45
|
+
import tempfile
|
44
46
|
import threading
|
45
47
|
import time
|
46
48
|
import traceback
|
@@ -98,6 +100,10 @@ CallableVersionOperator = ta.Callable[['Version', str], bool]
|
|
98
100
|
CommandT = ta.TypeVar('CommandT', bound='Command')
|
99
101
|
CommandOutputT = ta.TypeVar('CommandOutputT', bound='Command.Output')
|
100
102
|
|
103
|
+
# deploy/atomics.py
|
104
|
+
DeployAtomicPathSwapKind = ta.Literal['dir', 'file']
|
105
|
+
DeployAtomicPathSwapState = ta.Literal['open', 'committed', 'aborted'] # ta.TypeAlias
|
106
|
+
|
101
107
|
# deploy/paths.py
|
102
108
|
DeployPathKind = ta.Literal['dir', 'file'] # ta.TypeAlias
|
103
109
|
DeployPathPlaceholder = ta.Literal['app', 'tag'] # ta.TypeAlias
|
@@ -1382,6 +1388,7 @@ DeployHome = ta.NewType('DeployHome', str)
|
|
1382
1388
|
DeployApp = ta.NewType('DeployApp', str)
|
1383
1389
|
DeployTag = ta.NewType('DeployTag', str)
|
1384
1390
|
DeployRev = ta.NewType('DeployRev', str)
|
1391
|
+
DeployKey = ta.NewType('DeployKey', str)
|
1385
1392
|
|
1386
1393
|
|
1387
1394
|
class DeployAppTag(ta.NamedTuple):
|
@@ -4053,6 +4060,204 @@ def build_command_name_map(crs: CommandRegistrations) -> CommandNameMap:
|
|
4053
4060
|
return CommandNameMap(dct)
|
4054
4061
|
|
4055
4062
|
|
4063
|
+
########################################
|
4064
|
+
# ../deploy/atomics.py
|
4065
|
+
|
4066
|
+
|
4067
|
+
##
|
4068
|
+
|
4069
|
+
|
4070
|
+
class DeployAtomicPathSwap(abc.ABC):
|
4071
|
+
def __init__(
|
4072
|
+
self,
|
4073
|
+
kind: DeployAtomicPathSwapKind,
|
4074
|
+
dst_path: str,
|
4075
|
+
*,
|
4076
|
+
auto_commit: bool = False,
|
4077
|
+
) -> None:
|
4078
|
+
super().__init__()
|
4079
|
+
|
4080
|
+
self._kind = kind
|
4081
|
+
self._dst_path = dst_path
|
4082
|
+
self._auto_commit = auto_commit
|
4083
|
+
|
4084
|
+
self._state: DeployAtomicPathSwapState = 'open'
|
4085
|
+
|
4086
|
+
def __repr__(self) -> str:
|
4087
|
+
return attr_repr(self, 'kind', 'dst_path', 'tmp_path')
|
4088
|
+
|
4089
|
+
@property
|
4090
|
+
def kind(self) -> DeployAtomicPathSwapKind:
|
4091
|
+
return self._kind
|
4092
|
+
|
4093
|
+
@property
|
4094
|
+
def dst_path(self) -> str:
|
4095
|
+
return self._dst_path
|
4096
|
+
|
4097
|
+
@property
|
4098
|
+
@abc.abstractmethod
|
4099
|
+
def tmp_path(self) -> str:
|
4100
|
+
raise NotImplementedError
|
4101
|
+
|
4102
|
+
#
|
4103
|
+
|
4104
|
+
@property
|
4105
|
+
def state(self) -> DeployAtomicPathSwapState:
|
4106
|
+
return self._state
|
4107
|
+
|
4108
|
+
def _check_state(self, *states: DeployAtomicPathSwapState) -> None:
|
4109
|
+
if self._state not in states:
|
4110
|
+
raise RuntimeError(f'Atomic path swap not in correct state: {self._state}, {states}')
|
4111
|
+
|
4112
|
+
#
|
4113
|
+
|
4114
|
+
@abc.abstractmethod
|
4115
|
+
def _commit(self) -> None:
|
4116
|
+
raise NotImplementedError
|
4117
|
+
|
4118
|
+
def commit(self) -> None:
|
4119
|
+
if self._state == 'committed':
|
4120
|
+
return
|
4121
|
+
self._check_state('open')
|
4122
|
+
try:
|
4123
|
+
self._commit()
|
4124
|
+
except Exception: # noqa
|
4125
|
+
self._abort()
|
4126
|
+
raise
|
4127
|
+
else:
|
4128
|
+
self._state = 'committed'
|
4129
|
+
|
4130
|
+
#
|
4131
|
+
|
4132
|
+
@abc.abstractmethod
|
4133
|
+
def _abort(self) -> None:
|
4134
|
+
raise NotImplementedError
|
4135
|
+
|
4136
|
+
def abort(self) -> None:
|
4137
|
+
if self._state == 'aborted':
|
4138
|
+
return
|
4139
|
+
self._abort()
|
4140
|
+
self._state = 'aborted'
|
4141
|
+
|
4142
|
+
#
|
4143
|
+
|
4144
|
+
def __enter__(self) -> 'DeployAtomicPathSwap':
|
4145
|
+
return self
|
4146
|
+
|
4147
|
+
def __exit__(self, exc_type, exc_val, exc_tb):
|
4148
|
+
if (
|
4149
|
+
exc_type is None and
|
4150
|
+
self._auto_commit and
|
4151
|
+
self._state == 'open'
|
4152
|
+
):
|
4153
|
+
self.commit()
|
4154
|
+
else:
|
4155
|
+
self.abort()
|
4156
|
+
|
4157
|
+
|
4158
|
+
#
|
4159
|
+
|
4160
|
+
|
4161
|
+
class DeployAtomicPathSwapping(abc.ABC):
|
4162
|
+
@abc.abstractmethod
|
4163
|
+
def begin_atomic_path_swap(
|
4164
|
+
self,
|
4165
|
+
kind: DeployAtomicPathSwapKind,
|
4166
|
+
dst_path: str,
|
4167
|
+
*,
|
4168
|
+
name_hint: ta.Optional[str] = None,
|
4169
|
+
make_dirs: bool = False,
|
4170
|
+
**kwargs: ta.Any,
|
4171
|
+
) -> DeployAtomicPathSwap:
|
4172
|
+
raise NotImplementedError
|
4173
|
+
|
4174
|
+
|
4175
|
+
##
|
4176
|
+
|
4177
|
+
|
4178
|
+
class OsRenameDeployAtomicPathSwap(DeployAtomicPathSwap):
|
4179
|
+
def __init__(
|
4180
|
+
self,
|
4181
|
+
kind: DeployAtomicPathSwapKind,
|
4182
|
+
dst_path: str,
|
4183
|
+
tmp_path: str,
|
4184
|
+
**kwargs: ta.Any,
|
4185
|
+
) -> None:
|
4186
|
+
if kind == 'dir':
|
4187
|
+
check.state(os.path.isdir(tmp_path))
|
4188
|
+
elif kind == 'file':
|
4189
|
+
check.state(os.path.isfile(tmp_path))
|
4190
|
+
else:
|
4191
|
+
raise TypeError(kind)
|
4192
|
+
|
4193
|
+
super().__init__(
|
4194
|
+
kind,
|
4195
|
+
dst_path,
|
4196
|
+
**kwargs,
|
4197
|
+
)
|
4198
|
+
|
4199
|
+
self._tmp_path = tmp_path
|
4200
|
+
|
4201
|
+
@property
|
4202
|
+
def tmp_path(self) -> str:
|
4203
|
+
return self._tmp_path
|
4204
|
+
|
4205
|
+
def _commit(self) -> None:
|
4206
|
+
os.rename(self._tmp_path, self._dst_path)
|
4207
|
+
|
4208
|
+
def _abort(self) -> None:
|
4209
|
+
shutil.rmtree(self._tmp_path, ignore_errors=True)
|
4210
|
+
|
4211
|
+
|
4212
|
+
class TempDirDeployAtomicPathSwapping(DeployAtomicPathSwapping):
|
4213
|
+
def __init__(
|
4214
|
+
self,
|
4215
|
+
*,
|
4216
|
+
temp_dir: ta.Optional[str] = None,
|
4217
|
+
root_dir: ta.Optional[str] = None,
|
4218
|
+
) -> None:
|
4219
|
+
super().__init__()
|
4220
|
+
|
4221
|
+
if root_dir is not None:
|
4222
|
+
root_dir = os.path.abspath(root_dir)
|
4223
|
+
self._root_dir = root_dir
|
4224
|
+
self._temp_dir = temp_dir
|
4225
|
+
|
4226
|
+
def begin_atomic_path_swap(
|
4227
|
+
self,
|
4228
|
+
kind: DeployAtomicPathSwapKind,
|
4229
|
+
dst_path: str,
|
4230
|
+
*,
|
4231
|
+
name_hint: ta.Optional[str] = None,
|
4232
|
+
make_dirs: bool = False,
|
4233
|
+
**kwargs: ta.Any,
|
4234
|
+
) -> DeployAtomicPathSwap:
|
4235
|
+
dst_path = os.path.abspath(dst_path)
|
4236
|
+
if self._root_dir is not None and not dst_path.startswith(check.non_empty_str(self._root_dir)):
|
4237
|
+
raise RuntimeError(f'Atomic path swap dst must be in root dir: {dst_path}, {self._root_dir}')
|
4238
|
+
|
4239
|
+
dst_dir = os.path.dirname(dst_path)
|
4240
|
+
if make_dirs:
|
4241
|
+
os.makedirs(dst_dir, exist_ok=True)
|
4242
|
+
if not os.path.isdir(dst_dir):
|
4243
|
+
raise RuntimeError(f'Atomic path swap dst dir does not exist: {dst_dir}')
|
4244
|
+
|
4245
|
+
if kind == 'dir':
|
4246
|
+
tmp_path = tempfile.mkdtemp(prefix=name_hint, dir=self._temp_dir)
|
4247
|
+
elif kind == 'file':
|
4248
|
+
fd, tmp_path = tempfile.mkstemp(prefix=name_hint, dir=self._temp_dir)
|
4249
|
+
os.close(fd)
|
4250
|
+
else:
|
4251
|
+
raise TypeError(kind)
|
4252
|
+
|
4253
|
+
return OsRenameDeployAtomicPathSwap(
|
4254
|
+
kind,
|
4255
|
+
dst_path,
|
4256
|
+
tmp_path,
|
4257
|
+
**kwargs,
|
4258
|
+
)
|
4259
|
+
|
4260
|
+
|
4056
4261
|
########################################
|
4057
4262
|
# ../deploy/paths.py
|
4058
4263
|
"""
|
@@ -4070,6 +4275,8 @@ def build_command_name_map(crs: CommandRegistrations) -> CommandNameMap:
|
|
4070
4275
|
/venv
|
4071
4276
|
/<appplaceholder>
|
4072
4277
|
|
4278
|
+
/tmp
|
4279
|
+
|
4073
4280
|
?
|
4074
4281
|
/logs
|
4075
4282
|
/wrmsr--omlish--<placeholder>
|
@@ -4227,6 +4434,8 @@ class DeployPath:
|
|
4227
4434
|
parts: ta.Sequence[DeployPathPart]
|
4228
4435
|
|
4229
4436
|
def __post_init__(self) -> None:
|
4437
|
+
hash(self)
|
4438
|
+
|
4230
4439
|
check.not_empty(self.parts)
|
4231
4440
|
for p in self.parts[:-1]:
|
4232
4441
|
check.equal(p.kind, 'dir')
|
@@ -4261,10 +4470,10 @@ class DeployPath:
|
|
4261
4470
|
else:
|
4262
4471
|
tail_parse = FileDeployPathPart.parse
|
4263
4472
|
ps = check.non_empty_str(s).split('/')
|
4264
|
-
return cls(
|
4473
|
+
return cls((
|
4265
4474
|
*([DirDeployPathPart.parse(p) for p in ps[:-1]] if len(ps) > 1 else []),
|
4266
4475
|
tail_parse(ps[-1]),
|
4267
|
-
|
4476
|
+
))
|
4268
4477
|
|
4269
4478
|
|
4270
4479
|
##
|
@@ -4272,10 +4481,41 @@ class DeployPath:
|
|
4272
4481
|
|
4273
4482
|
class DeployPathOwner(abc.ABC):
|
4274
4483
|
@abc.abstractmethod
|
4275
|
-
def
|
4484
|
+
def get_owned_deploy_paths(self) -> ta.AbstractSet[DeployPath]:
|
4276
4485
|
raise NotImplementedError
|
4277
4486
|
|
4278
4487
|
|
4488
|
+
class SingleDirDeployPathOwner(DeployPathOwner, abc.ABC):
|
4489
|
+
def __init__(
|
4490
|
+
self,
|
4491
|
+
*args: ta.Any,
|
4492
|
+
owned_dir: str,
|
4493
|
+
deploy_home: ta.Optional[DeployHome],
|
4494
|
+
**kwargs: ta.Any,
|
4495
|
+
) -> None:
|
4496
|
+
super().__init__(*args, **kwargs)
|
4497
|
+
|
4498
|
+
check.not_in('/', owned_dir)
|
4499
|
+
self._owned_dir: str = check.non_empty_str(owned_dir)
|
4500
|
+
|
4501
|
+
self._deploy_home = deploy_home
|
4502
|
+
|
4503
|
+
self._owned_deploy_paths = frozenset([DeployPath.parse(self._owned_dir + '/')])
|
4504
|
+
|
4505
|
+
@cached_nullary
|
4506
|
+
def _dir(self) -> str:
|
4507
|
+
return os.path.join(check.non_empty_str(self._deploy_home), self._owned_dir)
|
4508
|
+
|
4509
|
+
@cached_nullary
|
4510
|
+
def _make_dir(self) -> str:
|
4511
|
+
if not os.path.isdir(d := self._dir()):
|
4512
|
+
os.makedirs(d, exist_ok=True)
|
4513
|
+
return d
|
4514
|
+
|
4515
|
+
def get_owned_deploy_paths(self) -> ta.AbstractSet[DeployPath]:
|
4516
|
+
return self._owned_deploy_paths
|
4517
|
+
|
4518
|
+
|
4279
4519
|
########################################
|
4280
4520
|
# ../deploy/specs.py
|
4281
4521
|
|
@@ -4303,6 +4543,13 @@ class DeploySpec:
|
|
4303
4543
|
repo: DeployGitRepo
|
4304
4544
|
rev: DeployRev
|
4305
4545
|
|
4546
|
+
def __post_init__(self) -> None:
|
4547
|
+
hash(self)
|
4548
|
+
|
4549
|
+
@cached_nullary
|
4550
|
+
def key(self) -> DeployKey:
|
4551
|
+
return DeployKey(hashlib.sha256(repr(self).encode('utf-8')).hexdigest()[:8])
|
4552
|
+
|
4306
4553
|
|
4307
4554
|
########################################
|
4308
4555
|
# ../remote/config.py
|
@@ -6210,12 +6457,12 @@ def is_debugger_attached() -> bool:
|
|
6210
6457
|
return any(frame[1].endswith('pydevd.py') for frame in inspect.stack())
|
6211
6458
|
|
6212
6459
|
|
6213
|
-
|
6460
|
+
LITE_REQUIRED_PYTHON_VERSION = (3, 8)
|
6214
6461
|
|
6215
6462
|
|
6216
|
-
def
|
6217
|
-
if sys.version_info <
|
6218
|
-
raise OSError(f'Requires python {
|
6463
|
+
def check_lite_runtime_version() -> None:
|
6464
|
+
if sys.version_info < LITE_REQUIRED_PYTHON_VERSION:
|
6465
|
+
raise OSError(f'Requires python {LITE_REQUIRED_PYTHON_VERSION}, got {sys.version_info} from {sys.executable}') # noqa
|
6219
6466
|
|
6220
6467
|
|
6221
6468
|
########################################
|
@@ -6523,6 +6770,44 @@ class DeployCommandExecutor(CommandExecutor[DeployCommand, DeployCommand.Output]
|
|
6523
6770
|
return DeployCommand.Output()
|
6524
6771
|
|
6525
6772
|
|
6773
|
+
########################################
|
6774
|
+
# ../deploy/tmp.py
|
6775
|
+
|
6776
|
+
|
6777
|
+
class DeployTmpManager(
|
6778
|
+
SingleDirDeployPathOwner,
|
6779
|
+
DeployAtomicPathSwapping,
|
6780
|
+
):
|
6781
|
+
def __init__(
|
6782
|
+
self,
|
6783
|
+
*,
|
6784
|
+
deploy_home: ta.Optional[DeployHome] = None,
|
6785
|
+
) -> None:
|
6786
|
+
super().__init__(
|
6787
|
+
owned_dir='tmp',
|
6788
|
+
deploy_home=deploy_home,
|
6789
|
+
)
|
6790
|
+
|
6791
|
+
@cached_nullary
|
6792
|
+
def _swapping(self) -> DeployAtomicPathSwapping:
|
6793
|
+
return TempDirDeployAtomicPathSwapping(
|
6794
|
+
temp_dir=self._make_dir(),
|
6795
|
+
root_dir=check.non_empty_str(self._deploy_home),
|
6796
|
+
)
|
6797
|
+
|
6798
|
+
def begin_atomic_path_swap(
|
6799
|
+
self,
|
6800
|
+
kind: DeployAtomicPathSwapKind,
|
6801
|
+
dst_path: str,
|
6802
|
+
**kwargs: ta.Any,
|
6803
|
+
) -> DeployAtomicPathSwap:
|
6804
|
+
return self._swapping().begin_atomic_path_swap(
|
6805
|
+
kind,
|
6806
|
+
dst_path,
|
6807
|
+
**kwargs,
|
6808
|
+
)
|
6809
|
+
|
6810
|
+
|
6526
6811
|
########################################
|
6527
6812
|
# ../marshal.py
|
6528
6813
|
|
@@ -6630,6 +6915,7 @@ TODO:
|
|
6630
6915
|
- structured
|
6631
6916
|
- prefixed
|
6632
6917
|
- debug
|
6918
|
+
- optional noisy? noisy will never be lite - some kinda configure_standard callback mechanism?
|
6633
6919
|
"""
|
6634
6920
|
|
6635
6921
|
|
@@ -6666,8 +6952,9 @@ class StandardLogFormatter(logging.Formatter):
|
|
6666
6952
|
##
|
6667
6953
|
|
6668
6954
|
|
6669
|
-
class
|
6670
|
-
|
6955
|
+
class StandardConfiguredLogHandler(ProxyLogHandler):
|
6956
|
+
def __init_subclass__(cls, **kwargs):
|
6957
|
+
raise TypeError('This class serves only as a marker and should not be subclassed.')
|
6671
6958
|
|
6672
6959
|
|
6673
6960
|
##
|
@@ -6698,7 +6985,7 @@ def configure_standard_logging(
|
|
6698
6985
|
target: ta.Optional[logging.Logger] = None,
|
6699
6986
|
force: bool = False,
|
6700
6987
|
handler_factory: ta.Optional[ta.Callable[[], logging.Handler]] = None,
|
6701
|
-
) -> ta.Optional[
|
6988
|
+
) -> ta.Optional[StandardConfiguredLogHandler]:
|
6702
6989
|
with _locking_logging_module_lock():
|
6703
6990
|
if target is None:
|
6704
6991
|
target = logging.root
|
@@ -6706,7 +6993,7 @@ def configure_standard_logging(
|
|
6706
6993
|
#
|
6707
6994
|
|
6708
6995
|
if not force:
|
6709
|
-
if any(isinstance(h,
|
6996
|
+
if any(isinstance(h, StandardConfiguredLogHandler) for h in list(target.handlers)):
|
6710
6997
|
return None
|
6711
6998
|
|
6712
6999
|
#
|
@@ -6740,7 +7027,7 @@ def configure_standard_logging(
|
|
6740
7027
|
|
6741
7028
|
#
|
6742
7029
|
|
6743
|
-
return
|
7030
|
+
return StandardConfiguredLogHandler(handler)
|
6744
7031
|
|
6745
7032
|
|
6746
7033
|
########################################
|
@@ -7907,27 +8194,22 @@ github.com/wrmsr/omlish@rev
|
|
7907
8194
|
##
|
7908
8195
|
|
7909
8196
|
|
7910
|
-
class DeployGitManager(
|
8197
|
+
class DeployGitManager(SingleDirDeployPathOwner):
|
7911
8198
|
def __init__(
|
7912
8199
|
self,
|
7913
8200
|
*,
|
7914
8201
|
deploy_home: ta.Optional[DeployHome] = None,
|
8202
|
+
atomics: DeployAtomicPathSwapping,
|
7915
8203
|
) -> None:
|
7916
|
-
super().__init__(
|
8204
|
+
super().__init__(
|
8205
|
+
owned_dir='git',
|
8206
|
+
deploy_home=deploy_home,
|
8207
|
+
)
|
7917
8208
|
|
7918
|
-
self.
|
8209
|
+
self._atomics = atomics
|
7919
8210
|
|
7920
8211
|
self._repo_dirs: ta.Dict[DeployGitRepo, DeployGitManager.RepoDir] = {}
|
7921
8212
|
|
7922
|
-
@cached_nullary
|
7923
|
-
def _dir(self) -> str:
|
7924
|
-
return os.path.join(check.non_empty_str(self._deploy_home), 'git')
|
7925
|
-
|
7926
|
-
def get_deploy_paths(self) -> ta.AbstractSet[DeployPath]:
|
7927
|
-
return {
|
7928
|
-
DeployPath.parse('git'),
|
7929
|
-
}
|
7930
|
-
|
7931
8213
|
class RepoDir:
|
7932
8214
|
def __init__(
|
7933
8215
|
self,
|
@@ -7939,7 +8221,7 @@ class DeployGitManager(DeployPathOwner):
|
|
7939
8221
|
self._git = git
|
7940
8222
|
self._repo = repo
|
7941
8223
|
self._dir = os.path.join(
|
7942
|
-
self._git.
|
8224
|
+
self._git._make_dir(), # noqa
|
7943
8225
|
check.non_empty_str(repo.host),
|
7944
8226
|
check.non_empty_str(repo.path),
|
7945
8227
|
)
|
@@ -7976,18 +8258,20 @@ class DeployGitManager(DeployPathOwner):
|
|
7976
8258
|
|
7977
8259
|
async def checkout(self, rev: DeployRev, dst_dir: str) -> None:
|
7978
8260
|
check.state(not os.path.exists(dst_dir))
|
8261
|
+
with self._git._atomics.begin_atomic_path_swap( # noqa
|
8262
|
+
'dir',
|
8263
|
+
dst_dir,
|
8264
|
+
auto_commit=True,
|
8265
|
+
make_dirs=True,
|
8266
|
+
) as dst_swap:
|
8267
|
+
await self.fetch(rev)
|
7979
8268
|
|
7980
|
-
|
8269
|
+
dst_call = functools.partial(asyncio_subprocesses.check_call, cwd=dst_swap.tmp_path)
|
8270
|
+
await dst_call('git', 'init')
|
7981
8271
|
|
7982
|
-
|
7983
|
-
|
7984
|
-
|
7985
|
-
dst_call = functools.partial(asyncio_subprocesses.check_call, cwd=dst_dir)
|
7986
|
-
await dst_call('git', 'init')
|
7987
|
-
|
7988
|
-
await dst_call('git', 'remote', 'add', 'local', self._dir)
|
7989
|
-
await dst_call('git', 'fetch', '--depth=1', 'local', rev)
|
7990
|
-
await dst_call('git', 'checkout', rev)
|
8272
|
+
await dst_call('git', 'remote', 'add', 'local', self._dir)
|
8273
|
+
await dst_call('git', 'fetch', '--depth=1', 'local', rev)
|
8274
|
+
await dst_call('git', 'checkout', rev)
|
7991
8275
|
|
7992
8276
|
def get_repo_dir(self, repo: DeployGitRepo) -> RepoDir:
|
7993
8277
|
try:
|
@@ -8014,16 +8298,18 @@ class DeployVenvManager(DeployPathOwner):
|
|
8014
8298
|
self,
|
8015
8299
|
*,
|
8016
8300
|
deploy_home: ta.Optional[DeployHome] = None,
|
8301
|
+
atomics: DeployAtomicPathSwapping,
|
8017
8302
|
) -> None:
|
8018
8303
|
super().__init__()
|
8019
8304
|
|
8020
8305
|
self._deploy_home = deploy_home
|
8306
|
+
self._atomics = atomics
|
8021
8307
|
|
8022
8308
|
@cached_nullary
|
8023
8309
|
def _dir(self) -> str:
|
8024
8310
|
return os.path.join(check.non_empty_str(self._deploy_home), 'venvs')
|
8025
8311
|
|
8026
|
-
def
|
8312
|
+
def get_owned_deploy_paths(self) -> ta.AbstractSet[DeployPath]:
|
8027
8313
|
return {
|
8028
8314
|
DeployPath.parse('venvs/@app/@tag/'),
|
8029
8315
|
}
|
@@ -8037,6 +8323,8 @@ class DeployVenvManager(DeployPathOwner):
|
|
8037
8323
|
) -> None:
|
8038
8324
|
sys_exe = 'python3'
|
8039
8325
|
|
8326
|
+
# !! NOTE: (most) venvs cannot be relocated, so an atomic swap can't be used. it's up to the path manager to
|
8327
|
+
# garbage collect orphaned dirs.
|
8040
8328
|
await asyncio_subprocesses.check_call(sys_exe, '-m', 'venv', venv_dir)
|
8041
8329
|
|
8042
8330
|
#
|
@@ -8594,13 +8882,15 @@ def bind_commands(
|
|
8594
8882
|
|
8595
8883
|
def make_deploy_tag(
|
8596
8884
|
rev: DeployRev,
|
8597
|
-
|
8885
|
+
key: DeployKey,
|
8886
|
+
*,
|
8887
|
+
utcnow: ta.Optional[datetime.datetime] = None,
|
8598
8888
|
) -> DeployTag:
|
8599
|
-
if
|
8600
|
-
|
8601
|
-
now_fmt = '%Y%m%dT%H%M%
|
8602
|
-
now_str =
|
8603
|
-
return DeployTag('-'.join([now_str, rev]))
|
8889
|
+
if utcnow is None:
|
8890
|
+
utcnow = datetime.datetime.now(tz=datetime.timezone.utc) # noqa
|
8891
|
+
now_fmt = '%Y%m%dT%H%M%SZ'
|
8892
|
+
now_str = utcnow.strftime(now_fmt)
|
8893
|
+
return DeployTag('-'.join([now_str, rev, key]))
|
8604
8894
|
|
8605
8895
|
|
8606
8896
|
class DeployAppManager(DeployPathOwner):
|
@@ -8621,7 +8911,7 @@ class DeployAppManager(DeployPathOwner):
|
|
8621
8911
|
def _dir(self) -> str:
|
8622
8912
|
return os.path.join(check.non_empty_str(self._deploy_home), 'apps')
|
8623
8913
|
|
8624
|
-
def
|
8914
|
+
def get_owned_deploy_paths(self) -> ta.AbstractSet[DeployPath]:
|
8625
8915
|
return {
|
8626
8916
|
DeployPath.parse('apps/@app/@tag'),
|
8627
8917
|
}
|
@@ -8630,7 +8920,7 @@ class DeployAppManager(DeployPathOwner):
|
|
8630
8920
|
self,
|
8631
8921
|
spec: DeploySpec,
|
8632
8922
|
):
|
8633
|
-
app_tag = DeployAppTag(spec.app, make_deploy_tag(spec.rev))
|
8923
|
+
app_tag = DeployAppTag(spec.app, make_deploy_tag(spec.rev, spec.key()))
|
8634
8924
|
app_dir = os.path.join(self._dir(), spec.app, app_tag.tag)
|
8635
8925
|
|
8636
8926
|
#
|
@@ -9738,10 +10028,19 @@ def bind_deploy(
|
|
9738
10028
|
lst: ta.List[InjectorBindingOrBindings] = [
|
9739
10029
|
inj.bind(deploy_config),
|
9740
10030
|
|
10031
|
+
#
|
10032
|
+
|
9741
10033
|
inj.bind(DeployAppManager, singleton=True),
|
10034
|
+
|
9742
10035
|
inj.bind(DeployGitManager, singleton=True),
|
10036
|
+
|
10037
|
+
inj.bind(DeployTmpManager, singleton=True),
|
10038
|
+
inj.bind(DeployAtomicPathSwapping, to_key=DeployTmpManager),
|
10039
|
+
|
9743
10040
|
inj.bind(DeployVenvManager, singleton=True),
|
9744
10041
|
|
10042
|
+
#
|
10043
|
+
|
9745
10044
|
bind_command(DeployCommand, DeployCommandExecutor),
|
9746
10045
|
bind_command(InterpCommand, InterpCommandExecutor),
|
9747
10046
|
]
|
ominfra/scripts/supervisor.py
CHANGED
@@ -5303,12 +5303,12 @@ def is_debugger_attached() -> bool:
|
|
5303
5303
|
return any(frame[1].endswith('pydevd.py') for frame in inspect.stack())
|
5304
5304
|
|
5305
5305
|
|
5306
|
-
|
5306
|
+
LITE_REQUIRED_PYTHON_VERSION = (3, 8)
|
5307
5307
|
|
5308
5308
|
|
5309
|
-
def
|
5310
|
-
if sys.version_info <
|
5311
|
-
raise OSError(f'Requires python {
|
5309
|
+
def check_lite_runtime_version() -> None:
|
5310
|
+
if sys.version_info < LITE_REQUIRED_PYTHON_VERSION:
|
5311
|
+
raise OSError(f'Requires python {LITE_REQUIRED_PYTHON_VERSION}, got {sys.version_info} from {sys.executable}') # noqa
|
5312
5312
|
|
5313
5313
|
|
5314
5314
|
########################################
|
@@ -5760,6 +5760,7 @@ TODO:
|
|
5760
5760
|
- structured
|
5761
5761
|
- prefixed
|
5762
5762
|
- debug
|
5763
|
+
- optional noisy? noisy will never be lite - some kinda configure_standard callback mechanism?
|
5763
5764
|
"""
|
5764
5765
|
|
5765
5766
|
|
@@ -5796,8 +5797,9 @@ class StandardLogFormatter(logging.Formatter):
|
|
5796
5797
|
##
|
5797
5798
|
|
5798
5799
|
|
5799
|
-
class
|
5800
|
-
|
5800
|
+
class StandardConfiguredLogHandler(ProxyLogHandler):
|
5801
|
+
def __init_subclass__(cls, **kwargs):
|
5802
|
+
raise TypeError('This class serves only as a marker and should not be subclassed.')
|
5801
5803
|
|
5802
5804
|
|
5803
5805
|
##
|
@@ -5828,7 +5830,7 @@ def configure_standard_logging(
|
|
5828
5830
|
target: ta.Optional[logging.Logger] = None,
|
5829
5831
|
force: bool = False,
|
5830
5832
|
handler_factory: ta.Optional[ta.Callable[[], logging.Handler]] = None,
|
5831
|
-
) -> ta.Optional[
|
5833
|
+
) -> ta.Optional[StandardConfiguredLogHandler]:
|
5832
5834
|
with _locking_logging_module_lock():
|
5833
5835
|
if target is None:
|
5834
5836
|
target = logging.root
|
@@ -5836,7 +5838,7 @@ def configure_standard_logging(
|
|
5836
5838
|
#
|
5837
5839
|
|
5838
5840
|
if not force:
|
5839
|
-
if any(isinstance(h,
|
5841
|
+
if any(isinstance(h, StandardConfiguredLogHandler) for h in list(target.handlers)):
|
5840
5842
|
return None
|
5841
5843
|
|
5842
5844
|
#
|
@@ -5870,7 +5872,7 @@ def configure_standard_logging(
|
|
5870
5872
|
|
5871
5873
|
#
|
5872
5874
|
|
5873
|
-
return
|
5875
|
+
return StandardConfiguredLogHandler(handler)
|
5874
5876
|
|
5875
5877
|
|
5876
5878
|
########################################
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: ominfra
|
3
|
-
Version: 0.0.0.
|
3
|
+
Version: 0.0.0.dev159
|
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.dev159
|
16
|
+
Requires-Dist: omlish==0.0.0.dev159
|
17
17
|
Provides-Extra: all
|
18
18
|
Requires-Dist: paramiko~=3.5; extra == "all"
|
19
19
|
Requires-Dist: asyncssh~=2.18; extra == "all"
|