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/manage/deploy/apps.py
CHANGED
@@ -12,6 +12,7 @@ from .paths import DeployPathOwner
|
|
12
12
|
from .specs import DeploySpec
|
13
13
|
from .types import DeployAppTag
|
14
14
|
from .types import DeployHome
|
15
|
+
from .types import DeployKey
|
15
16
|
from .types import DeployRev
|
16
17
|
from .types import DeployTag
|
17
18
|
from .venvs import DeployVenvManager
|
@@ -19,13 +20,15 @@ from .venvs import DeployVenvManager
|
|
19
20
|
|
20
21
|
def make_deploy_tag(
|
21
22
|
rev: DeployRev,
|
22
|
-
|
23
|
+
key: DeployKey,
|
24
|
+
*,
|
25
|
+
utcnow: ta.Optional[datetime.datetime] = None,
|
23
26
|
) -> DeployTag:
|
24
|
-
if
|
25
|
-
|
26
|
-
now_fmt = '%Y%m%dT%H%M%
|
27
|
-
now_str =
|
28
|
-
return DeployTag('-'.join([now_str, rev]))
|
27
|
+
if utcnow is None:
|
28
|
+
utcnow = datetime.datetime.now(tz=datetime.timezone.utc) # noqa
|
29
|
+
now_fmt = '%Y%m%dT%H%M%SZ'
|
30
|
+
now_str = utcnow.strftime(now_fmt)
|
31
|
+
return DeployTag('-'.join([now_str, rev, key]))
|
29
32
|
|
30
33
|
|
31
34
|
class DeployAppManager(DeployPathOwner):
|
@@ -46,7 +49,7 @@ class DeployAppManager(DeployPathOwner):
|
|
46
49
|
def _dir(self) -> str:
|
47
50
|
return os.path.join(check.non_empty_str(self._deploy_home), 'apps')
|
48
51
|
|
49
|
-
def
|
52
|
+
def get_owned_deploy_paths(self) -> ta.AbstractSet[DeployPath]:
|
50
53
|
return {
|
51
54
|
DeployPath.parse('apps/@app/@tag'),
|
52
55
|
}
|
@@ -55,7 +58,7 @@ class DeployAppManager(DeployPathOwner):
|
|
55
58
|
self,
|
56
59
|
spec: DeploySpec,
|
57
60
|
):
|
58
|
-
app_tag = DeployAppTag(spec.app, make_deploy_tag(spec.rev))
|
61
|
+
app_tag = DeployAppTag(spec.app, make_deploy_tag(spec.rev, spec.key()))
|
59
62
|
app_dir = os.path.join(self._dir(), spec.app, app_tag.tag)
|
60
63
|
|
61
64
|
#
|
@@ -0,0 +1,207 @@
|
|
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
|
+
)
|
ominfra/manage/deploy/git.py
CHANGED
@@ -14,11 +14,10 @@ import typing as ta
|
|
14
14
|
|
15
15
|
from omlish.asyncs.asyncio.subprocesses import asyncio_subprocesses
|
16
16
|
from omlish.lite.cached import async_cached_nullary
|
17
|
-
from omlish.lite.cached import cached_nullary
|
18
17
|
from omlish.lite.check import check
|
19
18
|
|
20
|
-
from .
|
21
|
-
from .paths import
|
19
|
+
from .atomics import DeployAtomicPathSwapping
|
20
|
+
from .paths import SingleDirDeployPathOwner
|
22
21
|
from .specs import DeployGitRepo
|
23
22
|
from .types import DeployHome
|
24
23
|
from .types import DeployRev
|
@@ -27,27 +26,22 @@ from .types import DeployRev
|
|
27
26
|
##
|
28
27
|
|
29
28
|
|
30
|
-
class DeployGitManager(
|
29
|
+
class DeployGitManager(SingleDirDeployPathOwner):
|
31
30
|
def __init__(
|
32
31
|
self,
|
33
32
|
*,
|
34
33
|
deploy_home: ta.Optional[DeployHome] = None,
|
34
|
+
atomics: DeployAtomicPathSwapping,
|
35
35
|
) -> None:
|
36
|
-
super().__init__(
|
36
|
+
super().__init__(
|
37
|
+
owned_dir='git',
|
38
|
+
deploy_home=deploy_home,
|
39
|
+
)
|
37
40
|
|
38
|
-
self.
|
41
|
+
self._atomics = atomics
|
39
42
|
|
40
43
|
self._repo_dirs: ta.Dict[DeployGitRepo, DeployGitManager.RepoDir] = {}
|
41
44
|
|
42
|
-
@cached_nullary
|
43
|
-
def _dir(self) -> str:
|
44
|
-
return os.path.join(check.non_empty_str(self._deploy_home), 'git')
|
45
|
-
|
46
|
-
def get_deploy_paths(self) -> ta.AbstractSet[DeployPath]:
|
47
|
-
return {
|
48
|
-
DeployPath.parse('git'),
|
49
|
-
}
|
50
|
-
|
51
45
|
class RepoDir:
|
52
46
|
def __init__(
|
53
47
|
self,
|
@@ -59,7 +53,7 @@ class DeployGitManager(DeployPathOwner):
|
|
59
53
|
self._git = git
|
60
54
|
self._repo = repo
|
61
55
|
self._dir = os.path.join(
|
62
|
-
self._git.
|
56
|
+
self._git._make_dir(), # noqa
|
63
57
|
check.non_empty_str(repo.host),
|
64
58
|
check.non_empty_str(repo.path),
|
65
59
|
)
|
@@ -96,18 +90,20 @@ class DeployGitManager(DeployPathOwner):
|
|
96
90
|
|
97
91
|
async def checkout(self, rev: DeployRev, dst_dir: str) -> None:
|
98
92
|
check.state(not os.path.exists(dst_dir))
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
93
|
+
with self._git._atomics.begin_atomic_path_swap( # noqa
|
94
|
+
'dir',
|
95
|
+
dst_dir,
|
96
|
+
auto_commit=True,
|
97
|
+
make_dirs=True,
|
98
|
+
) as dst_swap:
|
99
|
+
await self.fetch(rev)
|
100
|
+
|
101
|
+
dst_call = functools.partial(asyncio_subprocesses.check_call, cwd=dst_swap.tmp_path)
|
102
|
+
await dst_call('git', 'init')
|
103
|
+
|
104
|
+
await dst_call('git', 'remote', 'add', 'local', self._dir)
|
105
|
+
await dst_call('git', 'fetch', '--depth=1', 'local', rev)
|
106
|
+
await dst_call('git', 'checkout', rev)
|
111
107
|
|
112
108
|
def get_repo_dir(self, repo: DeployGitRepo) -> RepoDir:
|
113
109
|
try:
|
ominfra/manage/deploy/inject.py
CHANGED
@@ -8,12 +8,14 @@ from omlish.lite.inject import inj
|
|
8
8
|
|
9
9
|
from ..commands.inject import bind_command
|
10
10
|
from .apps import DeployAppManager
|
11
|
+
from .atomics import DeployAtomicPathSwapping
|
11
12
|
from .commands import DeployCommand
|
12
13
|
from .commands import DeployCommandExecutor
|
13
14
|
from .config import DeployConfig
|
14
15
|
from .git import DeployGitManager
|
15
16
|
from .interp import InterpCommand
|
16
17
|
from .interp import InterpCommandExecutor
|
18
|
+
from .tmp import DeployTmpManager
|
17
19
|
from .types import DeployHome
|
18
20
|
from .venvs import DeployVenvManager
|
19
21
|
|
@@ -25,10 +27,19 @@ def bind_deploy(
|
|
25
27
|
lst: ta.List[InjectorBindingOrBindings] = [
|
26
28
|
inj.bind(deploy_config),
|
27
29
|
|
30
|
+
#
|
31
|
+
|
28
32
|
inj.bind(DeployAppManager, singleton=True),
|
33
|
+
|
29
34
|
inj.bind(DeployGitManager, singleton=True),
|
35
|
+
|
36
|
+
inj.bind(DeployTmpManager, singleton=True),
|
37
|
+
inj.bind(DeployAtomicPathSwapping, to_key=DeployTmpManager),
|
38
|
+
|
30
39
|
inj.bind(DeployVenvManager, singleton=True),
|
31
40
|
|
41
|
+
#
|
42
|
+
|
32
43
|
bind_command(DeployCommand, DeployCommandExecutor),
|
33
44
|
bind_command(InterpCommand, InterpCommandExecutor),
|
34
45
|
]
|
ominfra/manage/deploy/paths.py
CHANGED
@@ -14,6 +14,8 @@
|
|
14
14
|
/venv
|
15
15
|
/<appplaceholder>
|
16
16
|
|
17
|
+
/tmp
|
18
|
+
|
17
19
|
?
|
18
20
|
/logs
|
19
21
|
/wrmsr--omlish--<placeholder>
|
@@ -39,8 +41,11 @@ import dataclasses as dc
|
|
39
41
|
import os.path
|
40
42
|
import typing as ta
|
41
43
|
|
44
|
+
from omlish.lite.cached import cached_nullary
|
42
45
|
from omlish.lite.check import check
|
43
46
|
|
47
|
+
from .types import DeployHome
|
48
|
+
|
44
49
|
|
45
50
|
DeployPathKind = ta.Literal['dir', 'file'] # ta.TypeAlias
|
46
51
|
DeployPathPlaceholder = ta.Literal['app', 'tag'] # ta.TypeAlias
|
@@ -181,6 +186,8 @@ class DeployPath:
|
|
181
186
|
parts: ta.Sequence[DeployPathPart]
|
182
187
|
|
183
188
|
def __post_init__(self) -> None:
|
189
|
+
hash(self)
|
190
|
+
|
184
191
|
check.not_empty(self.parts)
|
185
192
|
for p in self.parts[:-1]:
|
186
193
|
check.equal(p.kind, 'dir')
|
@@ -215,10 +222,10 @@ class DeployPath:
|
|
215
222
|
else:
|
216
223
|
tail_parse = FileDeployPathPart.parse
|
217
224
|
ps = check.non_empty_str(s).split('/')
|
218
|
-
return cls(
|
225
|
+
return cls((
|
219
226
|
*([DirDeployPathPart.parse(p) for p in ps[:-1]] if len(ps) > 1 else []),
|
220
227
|
tail_parse(ps[-1]),
|
221
|
-
|
228
|
+
))
|
222
229
|
|
223
230
|
|
224
231
|
##
|
@@ -226,5 +233,36 @@ class DeployPath:
|
|
226
233
|
|
227
234
|
class DeployPathOwner(abc.ABC):
|
228
235
|
@abc.abstractmethod
|
229
|
-
def
|
236
|
+
def get_owned_deploy_paths(self) -> ta.AbstractSet[DeployPath]:
|
230
237
|
raise NotImplementedError
|
238
|
+
|
239
|
+
|
240
|
+
class SingleDirDeployPathOwner(DeployPathOwner, abc.ABC):
|
241
|
+
def __init__(
|
242
|
+
self,
|
243
|
+
*args: ta.Any,
|
244
|
+
owned_dir: str,
|
245
|
+
deploy_home: ta.Optional[DeployHome],
|
246
|
+
**kwargs: ta.Any,
|
247
|
+
) -> None:
|
248
|
+
super().__init__(*args, **kwargs)
|
249
|
+
|
250
|
+
check.not_in('/', owned_dir)
|
251
|
+
self._owned_dir: str = check.non_empty_str(owned_dir)
|
252
|
+
|
253
|
+
self._deploy_home = deploy_home
|
254
|
+
|
255
|
+
self._owned_deploy_paths = frozenset([DeployPath.parse(self._owned_dir + '/')])
|
256
|
+
|
257
|
+
@cached_nullary
|
258
|
+
def _dir(self) -> str:
|
259
|
+
return os.path.join(check.non_empty_str(self._deploy_home), self._owned_dir)
|
260
|
+
|
261
|
+
@cached_nullary
|
262
|
+
def _make_dir(self) -> str:
|
263
|
+
if not os.path.isdir(d := self._dir()):
|
264
|
+
os.makedirs(d, exist_ok=True)
|
265
|
+
return d
|
266
|
+
|
267
|
+
def get_owned_deploy_paths(self) -> ta.AbstractSet[DeployPath]:
|
268
|
+
return self._owned_deploy_paths
|
ominfra/manage/deploy/specs.py
CHANGED
@@ -1,10 +1,13 @@
|
|
1
1
|
# ruff: noqa: UP006 UP007
|
2
2
|
import dataclasses as dc
|
3
|
+
import hashlib
|
3
4
|
import typing as ta
|
4
5
|
|
6
|
+
from omlish.lite.cached import cached_nullary
|
5
7
|
from omlish.lite.check import check
|
6
8
|
|
7
9
|
from .types import DeployApp
|
10
|
+
from .types import DeployKey
|
8
11
|
from .types import DeployRev
|
9
12
|
|
10
13
|
|
@@ -30,3 +33,10 @@ class DeploySpec:
|
|
30
33
|
app: DeployApp
|
31
34
|
repo: DeployGitRepo
|
32
35
|
rev: DeployRev
|
36
|
+
|
37
|
+
def __post_init__(self) -> None:
|
38
|
+
hash(self)
|
39
|
+
|
40
|
+
@cached_nullary
|
41
|
+
def key(self) -> DeployKey:
|
42
|
+
return DeployKey(hashlib.sha256(repr(self).encode('utf-8')).hexdigest()[:8])
|
@@ -0,0 +1,46 @@
|
|
1
|
+
# ruff: noqa: UP006 UP007
|
2
|
+
import typing as ta
|
3
|
+
|
4
|
+
from omlish.lite.cached import cached_nullary
|
5
|
+
from omlish.lite.check import check
|
6
|
+
|
7
|
+
from .atomics import DeployAtomicPathSwap
|
8
|
+
from .atomics import DeployAtomicPathSwapKind
|
9
|
+
from .atomics import DeployAtomicPathSwapping
|
10
|
+
from .atomics import TempDirDeployAtomicPathSwapping
|
11
|
+
from .paths import SingleDirDeployPathOwner
|
12
|
+
from .types import DeployHome
|
13
|
+
|
14
|
+
|
15
|
+
class DeployTmpManager(
|
16
|
+
SingleDirDeployPathOwner,
|
17
|
+
DeployAtomicPathSwapping,
|
18
|
+
):
|
19
|
+
def __init__(
|
20
|
+
self,
|
21
|
+
*,
|
22
|
+
deploy_home: ta.Optional[DeployHome] = None,
|
23
|
+
) -> None:
|
24
|
+
super().__init__(
|
25
|
+
owned_dir='tmp',
|
26
|
+
deploy_home=deploy_home,
|
27
|
+
)
|
28
|
+
|
29
|
+
@cached_nullary
|
30
|
+
def _swapping(self) -> DeployAtomicPathSwapping:
|
31
|
+
return TempDirDeployAtomicPathSwapping(
|
32
|
+
temp_dir=self._make_dir(),
|
33
|
+
root_dir=check.non_empty_str(self._deploy_home),
|
34
|
+
)
|
35
|
+
|
36
|
+
def begin_atomic_path_swap(
|
37
|
+
self,
|
38
|
+
kind: DeployAtomicPathSwapKind,
|
39
|
+
dst_path: str,
|
40
|
+
**kwargs: ta.Any,
|
41
|
+
) -> DeployAtomicPathSwap:
|
42
|
+
return self._swapping().begin_atomic_path_swap(
|
43
|
+
kind,
|
44
|
+
dst_path,
|
45
|
+
**kwargs,
|
46
|
+
)
|
ominfra/manage/deploy/types.py
CHANGED
ominfra/manage/deploy/venvs.py
CHANGED
@@ -11,6 +11,7 @@ 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
13
|
|
14
|
+
from .atomics import DeployAtomicPathSwapping
|
14
15
|
from .paths import DeployPath
|
15
16
|
from .paths import DeployPathOwner
|
16
17
|
from .types import DeployAppTag
|
@@ -22,16 +23,18 @@ class DeployVenvManager(DeployPathOwner):
|
|
22
23
|
self,
|
23
24
|
*,
|
24
25
|
deploy_home: ta.Optional[DeployHome] = None,
|
26
|
+
atomics: DeployAtomicPathSwapping,
|
25
27
|
) -> None:
|
26
28
|
super().__init__()
|
27
29
|
|
28
30
|
self._deploy_home = deploy_home
|
31
|
+
self._atomics = atomics
|
29
32
|
|
30
33
|
@cached_nullary
|
31
34
|
def _dir(self) -> str:
|
32
35
|
return os.path.join(check.non_empty_str(self._deploy_home), 'venvs')
|
33
36
|
|
34
|
-
def
|
37
|
+
def get_owned_deploy_paths(self) -> ta.AbstractSet[DeployPath]:
|
35
38
|
return {
|
36
39
|
DeployPath.parse('venvs/@app/@tag/'),
|
37
40
|
}
|
@@ -45,6 +48,8 @@ class DeployVenvManager(DeployPathOwner):
|
|
45
48
|
) -> None:
|
46
49
|
sys_exe = 'python3'
|
47
50
|
|
51
|
+
# !! NOTE: (most) venvs cannot be relocated, so an atomic swap can't be used. it's up to the path manager to
|
52
|
+
# garbage collect orphaned dirs.
|
48
53
|
await asyncio_subprocesses.check_call(sys_exe, '-m', 'venv', venv_dir)
|
49
54
|
|
50
55
|
#
|
ominfra/scripts/journald2aws.py
CHANGED
@@ -2899,12 +2899,12 @@ def is_debugger_attached() -> bool:
|
|
2899
2899
|
return any(frame[1].endswith('pydevd.py') for frame in inspect.stack())
|
2900
2900
|
|
2901
2901
|
|
2902
|
-
|
2902
|
+
LITE_REQUIRED_PYTHON_VERSION = (3, 8)
|
2903
2903
|
|
2904
2904
|
|
2905
|
-
def
|
2906
|
-
if sys.version_info <
|
2907
|
-
raise OSError(f'Requires python {
|
2905
|
+
def check_lite_runtime_version() -> None:
|
2906
|
+
if sys.version_info < LITE_REQUIRED_PYTHON_VERSION:
|
2907
|
+
raise OSError(f'Requires python {LITE_REQUIRED_PYTHON_VERSION}, got {sys.version_info} from {sys.executable}') # noqa
|
2908
2908
|
|
2909
2909
|
|
2910
2910
|
########################################
|
@@ -3482,6 +3482,7 @@ TODO:
|
|
3482
3482
|
- structured
|
3483
3483
|
- prefixed
|
3484
3484
|
- debug
|
3485
|
+
- optional noisy? noisy will never be lite - some kinda configure_standard callback mechanism?
|
3485
3486
|
"""
|
3486
3487
|
|
3487
3488
|
|
@@ -3518,8 +3519,9 @@ class StandardLogFormatter(logging.Formatter):
|
|
3518
3519
|
##
|
3519
3520
|
|
3520
3521
|
|
3521
|
-
class
|
3522
|
-
|
3522
|
+
class StandardConfiguredLogHandler(ProxyLogHandler):
|
3523
|
+
def __init_subclass__(cls, **kwargs):
|
3524
|
+
raise TypeError('This class serves only as a marker and should not be subclassed.')
|
3523
3525
|
|
3524
3526
|
|
3525
3527
|
##
|
@@ -3550,7 +3552,7 @@ def configure_standard_logging(
|
|
3550
3552
|
target: ta.Optional[logging.Logger] = None,
|
3551
3553
|
force: bool = False,
|
3552
3554
|
handler_factory: ta.Optional[ta.Callable[[], logging.Handler]] = None,
|
3553
|
-
) -> ta.Optional[
|
3555
|
+
) -> ta.Optional[StandardConfiguredLogHandler]:
|
3554
3556
|
with _locking_logging_module_lock():
|
3555
3557
|
if target is None:
|
3556
3558
|
target = logging.root
|
@@ -3558,7 +3560,7 @@ def configure_standard_logging(
|
|
3558
3560
|
#
|
3559
3561
|
|
3560
3562
|
if not force:
|
3561
|
-
if any(isinstance(h,
|
3563
|
+
if any(isinstance(h, StandardConfiguredLogHandler) for h in list(target.handlers)):
|
3562
3564
|
return None
|
3563
3565
|
|
3564
3566
|
#
|
@@ -3592,7 +3594,7 @@ def configure_standard_logging(
|
|
3592
3594
|
|
3593
3595
|
#
|
3594
3596
|
|
3595
|
-
return
|
3597
|
+
return StandardConfiguredLogHandler(handler)
|
3596
3598
|
|
3597
3599
|
|
3598
3600
|
########################################
|