ominfra 0.0.0.dev189__py3-none-any.whl → 0.0.0.dev190__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/__about__.py CHANGED
@@ -10,6 +10,7 @@ class Project(ProjectBase):
10
10
  dependencies = [
11
11
  f'omdev == {__version__}',
12
12
  f'omlish == {__version__}',
13
+ f'omserv == {__version__}',
13
14
  ]
14
15
 
15
16
  optional_dependencies = {
@@ -1,10 +1,12 @@
1
1
  # ruff: noqa: UP006 UP007
2
+ import dataclasses as dc
2
3
  import os.path
3
4
  import typing as ta
4
5
 
5
6
  from omlish.lite.cached import cached_nullary
6
7
  from omlish.lite.check import check
7
- from omlish.os.paths import relative_symlink
8
+ from omlish.lite.json import json_dumps_pretty
9
+ from omlish.lite.marshal import ObjMarshalerManager
8
10
 
9
11
  from .conf.manager import DeployConfManager
10
12
  from .git import DeployGitManager
@@ -20,39 +22,31 @@ class DeployAppManager(DeployPathOwner):
20
22
  def __init__(
21
23
  self,
22
24
  *,
23
- conf: DeployConfManager,
24
25
  git: DeployGitManager,
25
26
  venvs: DeployVenvManager,
27
+ conf: DeployConfManager,
28
+
29
+ msh: ObjMarshalerManager,
26
30
  ) -> None:
27
31
  super().__init__()
28
32
 
29
- self._conf = conf
30
33
  self._git = git
31
34
  self._venvs = venvs
35
+ self._conf = conf
32
36
 
33
- #
34
-
35
- _APP_DIR_STR = 'apps/@app/@time--@app-rev--@app-key/'
36
- _APP_DIR = DeployPath.parse(_APP_DIR_STR)
37
+ self._msh = msh
37
38
 
38
- _DEPLOY_DIR_STR = 'deploys/@time--@deploy-key/'
39
- _DEPLOY_DIR = DeployPath.parse(_DEPLOY_DIR_STR)
39
+ #
40
40
 
41
- _APP_DEPLOY_LINK = DeployPath.parse(f'{_DEPLOY_DIR_STR}apps/@app')
42
- _CONF_DEPLOY_DIR = DeployPath.parse(f'{_DEPLOY_DIR_STR}conf/@conf/')
41
+ APP_DIR = DeployPath.parse('apps/@app/@time--@app-rev--@app-key/')
43
42
 
44
43
  @cached_nullary
45
44
  def get_owned_deploy_paths(self) -> ta.AbstractSet[DeployPath]:
46
45
  return {
47
- self._APP_DIR,
48
-
49
- self._DEPLOY_DIR,
50
-
51
- self._APP_DEPLOY_LINK,
52
- self._CONF_DEPLOY_DIR,
46
+ self.APP_DIR,
53
47
 
54
48
  *[
55
- DeployPath.parse(f'{self._APP_DIR_STR}{sfx}/')
49
+ DeployPath.parse(f'{self.APP_DIR}{sfx}/')
56
50
  for sfx in [
57
51
  'conf',
58
52
  'git',
@@ -63,115 +57,73 @@ class DeployAppManager(DeployPathOwner):
63
57
 
64
58
  #
65
59
 
60
+ @dc.dataclass(frozen=True)
61
+ class PreparedApp:
62
+ app_dir: str
63
+
64
+ git_dir: ta.Optional[str] = None
65
+ venv_dir: ta.Optional[str] = None
66
+ conf_dir: ta.Optional[str] = None
67
+
66
68
  async def prepare_app(
67
69
  self,
68
70
  spec: DeployAppSpec,
69
71
  home: DeployHome,
70
72
  tags: DeployTagMap,
71
- ) -> None:
72
- check.non_empty_str(home)
73
-
74
- def build_path(pth: DeployPath) -> str:
75
- return os.path.join(home, pth.render(tags))
76
-
77
- app_dir = build_path(self._APP_DIR)
78
- deploy_dir = build_path(self._DEPLOY_DIR)
79
- app_deploy_link = build_path(self._APP_DEPLOY_LINK)
73
+ ) -> PreparedApp:
74
+ spec_json = json_dumps_pretty(self._msh.marshal_obj(spec))
80
75
 
81
76
  #
82
77
 
83
- os.makedirs(deploy_dir, exist_ok=True)
84
-
85
- deploying_link = os.path.join(home, 'deploys/deploying')
86
- if os.path.exists(deploying_link):
87
- os.unlink(deploying_link)
88
- relative_symlink(
89
- deploy_dir,
90
- deploying_link,
91
- target_is_directory=True,
92
- make_dirs=True,
93
- )
78
+ check.non_empty_str(home)
94
79
 
95
- #
80
+ app_dir = os.path.join(home, self.APP_DIR.render(tags))
96
81
 
97
- os.makedirs(app_dir)
98
- relative_symlink(
99
- app_dir,
100
- app_deploy_link,
101
- target_is_directory=True,
102
- make_dirs=True,
103
- )
82
+ os.makedirs(app_dir, exist_ok=True)
104
83
 
105
84
  #
106
85
 
107
- deploy_conf_dir = os.path.join(deploy_dir, 'conf')
108
- os.makedirs(deploy_conf_dir, exist_ok=True)
86
+ rkw: ta.Dict[str, ta.Any] = dict(
87
+ app_dir=app_dir,
88
+ )
109
89
 
110
90
  #
111
91
 
112
- # def mirror_symlinks(src: str, dst: str) -> None:
113
- # def mirror_link(lp: str) -> None:
114
- # check.state(os.path.islink(lp))
115
- # shutil.copy2(
116
- # lp,
117
- # os.path.join(dst, os.path.relpath(lp, src)),
118
- # follow_symlinks=False,
119
- # )
120
- #
121
- # for dp, dns, fns in os.walk(src, followlinks=False):
122
- # for fn in fns:
123
- # mirror_link(os.path.join(dp, fn))
124
- #
125
- # for dn in dns:
126
- # dp2 = os.path.join(dp, dn)
127
- # if os.path.islink(dp2):
128
- # mirror_link(dp2)
129
- # else:
130
- # os.makedirs(os.path.join(dst, os.path.relpath(dp2, src)))
131
-
132
- current_link = os.path.join(home, 'deploys/current')
133
-
134
- # if os.path.exists(current_link):
135
- # mirror_symlinks(
136
- # os.path.join(current_link, 'conf'),
137
- # conf_tag_dir,
138
- # )
139
- # mirror_symlinks(
140
- # os.path.join(current_link, 'apps'),
141
- # os.path.join(deploy_dir, 'apps'),
142
- # )
92
+ spec_file = os.path.join(app_dir, 'spec.json')
93
+ with open(spec_file, 'w') as f: # noqa
94
+ f.write(spec_json)
143
95
 
144
96
  #
145
97
 
146
- app_git_dir = os.path.join(app_dir, 'git')
98
+ git_dir = os.path.join(app_dir, 'git')
99
+ rkw.update(git_dir=git_dir)
147
100
  await self._git.checkout(
148
101
  spec.git,
149
102
  home,
150
- app_git_dir,
103
+ git_dir,
151
104
  )
152
105
 
153
106
  #
154
107
 
155
108
  if spec.venv is not None:
156
- app_venv_dir = os.path.join(app_dir, 'venv')
109
+ venv_dir = os.path.join(app_dir, 'venv')
110
+ rkw.update(venv_dir=venv_dir)
157
111
  await self._venvs.setup_venv(
158
112
  spec.venv,
159
- home,
160
- app_git_dir,
161
- app_venv_dir,
113
+ git_dir,
114
+ venv_dir,
162
115
  )
163
116
 
164
117
  #
165
118
 
166
119
  if spec.conf is not None:
167
- app_conf_dir = os.path.join(app_dir, 'conf')
120
+ conf_dir = os.path.join(app_dir, 'conf')
121
+ rkw.update(conf_dir=conf_dir)
168
122
  await self._conf.write_app_conf(
169
123
  spec.conf,
170
- tags,
171
- app_conf_dir,
172
- deploy_conf_dir,
124
+ conf_dir,
173
125
  )
174
126
 
175
127
  #
176
128
 
177
- os.replace(deploying_link, current_link)
129
+ return DeployAppManager.PreparedApp(**rkw)
@@ -5,7 +5,7 @@ from omlish.lite.logs import log
5
5
 
6
6
  from ..commands.base import Command
7
7
  from ..commands.base import CommandExecutor
8
- from .driver import DeployDriverFactory
8
+ from .deploy import DeployDriverFactory
9
9
  from .specs import DeploySpec
10
10
 
11
11
 
@@ -24,6 +24,8 @@ from omlish.lite.json import json_dumps_pretty
24
24
  from omlish.lite.strings import strip_with_newline
25
25
  from omlish.os.paths import is_path_in_dir
26
26
  from omlish.os.paths import relative_symlink
27
+ from omserv.nginx.configs import NginxConfigItems
28
+ from omserv.nginx.configs import render_nginx_config_str
27
29
 
28
30
  from ....configs import render_ini_config
29
31
  from ..paths.paths import DeployPath
@@ -37,6 +39,7 @@ from .specs import DeployAppConfLink
37
39
  from .specs import DeployAppConfSpec
38
40
  from .specs import IniDeployAppConfContent
39
41
  from .specs import JsonDeployAppConfContent
42
+ from .specs import NginxDeployAppConfContent
40
43
  from .specs import RawDeployAppConfContent
41
44
 
42
45
 
@@ -51,6 +54,10 @@ class DeployConfManager:
51
54
  elif isinstance(ac, IniDeployAppConfContent):
52
55
  return strip_with_newline(render_ini_config(ac.sections))
53
56
 
57
+ elif isinstance(ac, NginxDeployAppConfContent):
58
+ ni = NginxConfigItems.of(ac.items)
59
+ return strip_with_newline(render_nginx_config_str(ni))
60
+
54
61
  else:
55
62
  raise TypeError(ac)
56
63
 
@@ -69,6 +76,17 @@ class DeployConfManager:
69
76
  with open(conf_file, 'w') as f: # noqa
70
77
  f.write(body)
71
78
 
79
+ async def write_app_conf(
80
+ self,
81
+ spec: DeployAppConfSpec,
82
+ app_conf_dir: str,
83
+ ) -> None:
84
+ for acf in spec.files or []:
85
+ await self._write_app_conf_file(
86
+ acf,
87
+ app_conf_dir,
88
+ )
89
+
72
90
  #
73
91
 
74
92
  class _ComputedConfLink(ta.NamedTuple):
@@ -178,23 +196,13 @@ class DeployConfManager:
178
196
  make_dirs=True,
179
197
  )
180
198
 
181
- #
182
-
183
- async def write_app_conf(
199
+ async def link_app_conf(
184
200
  self,
185
201
  spec: DeployAppConfSpec,
186
202
  tags: DeployTagMap,
187
203
  app_conf_dir: str,
188
204
  conf_link_dir: str,
189
- ) -> None:
190
- for acf in spec.files or []:
191
- await self._write_app_conf_file(
192
- acf,
193
- app_conf_dir,
194
- )
195
-
196
- #
197
-
205
+ ):
198
206
  for link in spec.links or []:
199
207
  await self._make_app_conf_link(
200
208
  link,
@@ -44,6 +44,15 @@ class IniDeployAppConfContent(DeployAppConfContent):
44
44
  sections: IniConfigSectionSettingsMap
45
45
 
46
46
 
47
+ #
48
+
49
+
50
+ @register_single_field_type_obj_marshaler('items')
51
+ @dc.dataclass(frozen=True)
52
+ class NginxDeployAppConfContent(DeployAppConfContent):
53
+ items: ta.Any
54
+
55
+
47
56
  ##
48
57
 
49
58
 
@@ -1,10 +1,31 @@
1
1
  # ruff: noqa: UP006 UP007
2
2
  import datetime
3
+ import os.path
3
4
  import typing as ta
4
5
 
6
+ from omlish.lite.cached import cached_nullary
7
+ from omlish.lite.check import check
8
+ from omlish.lite.json import json_dumps_pretty
9
+ from omlish.lite.marshal import ObjMarshalerManager
5
10
  from omlish.lite.typing import Func0
11
+ from omlish.lite.typing import Func1
12
+ from omlish.os.paths import relative_symlink
6
13
 
14
+ from .apps import DeployAppManager
15
+ from .conf.manager import DeployConfManager
16
+ from .paths.manager import DeployPathsManager
17
+ from .paths.owners import DeployPathOwner
18
+ from .paths.paths import DeployPath
19
+ from .specs import DeployAppSpec
20
+ from .specs import DeploySpec
21
+ from .systemd import DeploySystemdManager
22
+ from .tags import DeployAppRev
23
+ from .tags import DeployTagMap
7
24
  from .tags import DeployTime
25
+ from .types import DeployHome
26
+
27
+
28
+ ##
8
29
 
9
30
 
10
31
  DEPLOY_TAG_DATETIME_FMT = '%Y%m%dT%H%M%SZ'
@@ -13,17 +34,52 @@ DEPLOY_TAG_DATETIME_FMT = '%Y%m%dT%H%M%SZ'
13
34
  DeployManagerUtcClock = ta.NewType('DeployManagerUtcClock', Func0[datetime.datetime])
14
35
 
15
36
 
16
- class DeployManager:
37
+ class DeployManager(DeployPathOwner):
17
38
  def __init__(
18
39
  self,
19
40
  *,
20
-
21
41
  utc_clock: ta.Optional[DeployManagerUtcClock] = None,
22
42
  ):
23
43
  super().__init__()
24
44
 
25
45
  self._utc_clock = utc_clock
26
46
 
47
+ #
48
+
49
+ DEPLOYS_DIR = DeployPath.parse('deploys/')
50
+
51
+ CURRENT_DEPLOY_LINK = DeployPath.parse(f'{DEPLOYS_DIR}current')
52
+ DEPLOYING_DEPLOY_LINK = DeployPath.parse(f'{DEPLOYS_DIR}deploying')
53
+
54
+ DEPLOY_DIR = DeployPath.parse(f'{DEPLOYS_DIR}@time--@deploy-key/')
55
+ DEPLOY_SPEC_FILE = DeployPath.parse(f'{DEPLOY_DIR}spec.json')
56
+
57
+ APPS_DEPLOY_DIR = DeployPath.parse(f'{DEPLOY_DIR}apps/')
58
+ APP_DEPLOY_LINK = DeployPath.parse(f'{APPS_DEPLOY_DIR}@app')
59
+
60
+ CONFS_DEPLOY_DIR = DeployPath.parse(f'{DEPLOY_DIR}conf/')
61
+ CONF_DEPLOY_DIR = DeployPath.parse(f'{CONFS_DEPLOY_DIR}@conf/')
62
+
63
+ @cached_nullary
64
+ def get_owned_deploy_paths(self) -> ta.AbstractSet[DeployPath]:
65
+ return {
66
+ self.DEPLOYS_DIR,
67
+
68
+ self.CURRENT_DEPLOY_LINK,
69
+ self.DEPLOYING_DEPLOY_LINK,
70
+
71
+ self.DEPLOY_DIR,
72
+ self.DEPLOY_SPEC_FILE,
73
+
74
+ self.APPS_DEPLOY_DIR,
75
+ self.APP_DEPLOY_LINK,
76
+
77
+ self.CONFS_DEPLOY_DIR,
78
+ self.CONF_DEPLOY_DIR,
79
+ }
80
+
81
+ #
82
+
27
83
  def _utc_now(self) -> datetime.datetime:
28
84
  if self._utc_clock is not None:
29
85
  return self._utc_clock() # noqa
@@ -32,3 +88,152 @@ class DeployManager:
32
88
 
33
89
  def make_deploy_time(self) -> DeployTime:
34
90
  return DeployTime(self._utc_now().strftime(DEPLOY_TAG_DATETIME_FMT))
91
+
92
+
93
+ ##
94
+
95
+
96
+ class DeployDriverFactory(Func1[DeploySpec, ta.ContextManager['DeployDriver']]):
97
+ pass
98
+
99
+
100
+ class DeployDriver:
101
+ def __init__(
102
+ self,
103
+ *,
104
+ spec: DeploySpec,
105
+ home: DeployHome,
106
+ time: DeployTime,
107
+
108
+ deploys: DeployManager,
109
+ paths: DeployPathsManager,
110
+ apps: DeployAppManager,
111
+ conf: DeployConfManager,
112
+ systemd: DeploySystemdManager,
113
+
114
+ msh: ObjMarshalerManager,
115
+ ) -> None:
116
+ super().__init__()
117
+
118
+ self._spec = spec
119
+ self._home = home
120
+ self._time = time
121
+
122
+ self._deploys = deploys
123
+ self._paths = paths
124
+ self._apps = apps
125
+ self._conf = conf
126
+ self._systemd = systemd
127
+
128
+ self._msh = msh
129
+
130
+ #
131
+
132
+ @property
133
+ def deploy_tags(self) -> DeployTagMap:
134
+ return DeployTagMap(
135
+ self._time,
136
+ self._spec.key(),
137
+ )
138
+
139
+ def render_deploy_path(self, pth: DeployPath, tags: ta.Optional[DeployTagMap] = None) -> str:
140
+ return os.path.join(self._home, pth.render(tags if tags is not None else self.deploy_tags))
141
+
142
+ @property
143
+ def deploy_dir(self) -> str:
144
+ return self.render_deploy_path(self._deploys.DEPLOY_DIR)
145
+
146
+ #
147
+
148
+ async def drive_deploy(self) -> None:
149
+ spec_json = json_dumps_pretty(self._msh.marshal_obj(self._spec))
150
+
151
+ #
152
+
153
+ self._paths.validate_deploy_paths()
154
+
155
+ #
156
+
157
+ os.makedirs(self.deploy_dir)
158
+
159
+ #
160
+
161
+ spec_file = self.render_deploy_path(self._deploys.DEPLOY_SPEC_FILE)
162
+ with open(spec_file, 'w') as f: # noqa
163
+ f.write(spec_json)
164
+
165
+ #
166
+
167
+ deploying_link = self.render_deploy_path(self._deploys.DEPLOYING_DEPLOY_LINK)
168
+ if os.path.exists(deploying_link):
169
+ os.unlink(deploying_link)
170
+ relative_symlink(
171
+ self.deploy_dir,
172
+ deploying_link,
173
+ target_is_directory=True,
174
+ make_dirs=True,
175
+ )
176
+
177
+ #
178
+
179
+ for md in [
180
+ self._deploys.APPS_DEPLOY_DIR,
181
+ self._deploys.CONFS_DEPLOY_DIR,
182
+ ]:
183
+ os.makedirs(self.render_deploy_path(md))
184
+
185
+ #
186
+
187
+ for app in self._spec.apps:
188
+ await self.drive_app_deploy(app)
189
+
190
+ #
191
+
192
+ current_link = self.render_deploy_path(self._deploys.CURRENT_DEPLOY_LINK)
193
+ os.replace(deploying_link, current_link)
194
+
195
+ #
196
+
197
+ await self._systemd.sync_systemd(
198
+ self._spec.systemd,
199
+ self._home,
200
+ os.path.join(self.deploy_dir, 'conf', 'systemd'), # FIXME
201
+ )
202
+
203
+ #
204
+
205
+ async def drive_app_deploy(self, app: DeployAppSpec) -> None:
206
+ app_tags = self.deploy_tags.add(
207
+ app.app,
208
+ app.key(),
209
+ DeployAppRev(app.git.rev),
210
+ )
211
+
212
+ #
213
+
214
+ da = await self._apps.prepare_app(
215
+ app,
216
+ self._home,
217
+ app_tags,
218
+ )
219
+
220
+ #
221
+
222
+ app_link = self.render_deploy_path(self._deploys.APP_DEPLOY_LINK, app_tags)
223
+ relative_symlink(
224
+ da.app_dir,
225
+ app_link,
226
+ target_is_directory=True,
227
+ make_dirs=True,
228
+ )
229
+
230
+ #
231
+
232
+ deploy_conf_dir = self.render_deploy_path(self._deploys.CONFS_DEPLOY_DIR)
233
+ if app.conf is not None:
234
+ await self._conf.link_app_conf(
235
+ app.conf,
236
+ app_tags,
237
+ check.non_empty_str(da.conf_dir),
238
+ deploy_conf_dir,
239
+ )
@@ -16,15 +16,16 @@ from .commands import DeployCommand
16
16
  from .commands import DeployCommandExecutor
17
17
  from .conf.inject import bind_deploy_conf
18
18
  from .config import DeployConfig
19
+ from .deploy import DeployDriver
20
+ from .deploy import DeployDriverFactory
19
21
  from .deploy import DeployManager
20
- from .driver import DeployDriver
21
- from .driver import DeployDriverFactory
22
22
  from .git import DeployGitManager
23
23
  from .inject_ import bind_deploy_manager
24
24
  from .interp import InterpCommand
25
25
  from .interp import InterpCommandExecutor
26
26
  from .paths.inject import bind_deploy_paths
27
27
  from .specs import DeploySpec
28
+ from .systemd import DeploySystemdManager
28
29
  from .tags import DeployTime
29
30
  from .tmp import DeployHomeAtomics
30
31
  from .tmp import DeployTmpManager
@@ -101,13 +102,10 @@ def bind_deploy(
101
102
 
102
103
  lst.extend([
103
104
  bind_deploy_manager(DeployAppManager),
104
-
105
105
  bind_deploy_manager(DeployGitManager),
106
-
107
106
  bind_deploy_manager(DeployManager),
108
-
107
+ bind_deploy_manager(DeploySystemdManager),
109
108
  bind_deploy_manager(DeployTmpManager),
110
-
111
109
  bind_deploy_manager(DeployVenvManager),
112
110
  ])
113
111
 
@@ -0,0 +1,8 @@
1
+ # ruff: noqa: UP006 UP007
2
+ """
3
+ verify - nginx -t
4
+ """
5
+
6
+
7
+ class DeployNginxManager:
8
+ pass
@@ -33,6 +33,10 @@ class DeployPathError(Exception):
33
33
 
34
34
 
35
35
  class DeployPathRenderable(abc.ABC):
36
+ @cached_nullary
37
+ def __str__(self) -> str:
38
+ return self.render(None)
39
+
36
40
  @abc.abstractmethod
37
41
  def render(self, tags: ta.Optional[DeployTagMap] = None) -> str:
38
42
  raise NotImplementedError
@@ -174,7 +178,7 @@ class FileDeployPathPart(DeployPathPart):
174
178
 
175
179
 
176
180
  @dc.dataclass(frozen=True)
177
- class DeployPath:
181
+ class DeployPath(DeployPathRenderable):
178
182
  parts: ta.Sequence[DeployPathPart]
179
183
 
180
184
  @property
@@ -94,11 +94,22 @@ class DeployAppSpec(DeploySpecKeyed[DeployAppKey]):
94
94
  ##
95
95
 
96
96
 
97
+ @dc.dataclass(frozen=True)
98
+ class DeploySystemdSpec:
99
+ # ~/.config/systemd/user/
100
+ unit_dir: ta.Optional[str] = None
101
+
102
+
103
+ ##
104
+
105
+
97
106
  @dc.dataclass(frozen=True)
98
107
  class DeploySpec(DeploySpecKeyed[DeployKey]):
99
108
  home: DeployHome
100
109
 
101
- apps: ta.Sequence[DeployAppSpec]
110
+ apps: ta.Sequence[DeployAppSpec] = ()
111
+
112
+ systemd: ta.Optional[DeploySystemdSpec] = None
102
113
 
103
114
  def __post_init__(self) -> None:
104
115
  check.non_empty_str(self.home)