ominfra 0.0.0.dev189__py3-none-any.whl → 0.0.0.dev191__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,16 +1,20 @@
1
1
  # ruff: noqa: UP006 UP007
2
+ import dataclasses as dc
3
+ import json
2
4
  import os.path
3
5
  import typing as ta
4
6
 
5
7
  from omlish.lite.cached import cached_nullary
6
8
  from omlish.lite.check import check
7
- from omlish.os.paths import relative_symlink
9
+ from omlish.lite.json import json_dumps_pretty
10
+ from omlish.lite.marshal import ObjMarshalerManager
8
11
 
9
12
  from .conf.manager import DeployConfManager
10
13
  from .git import DeployGitManager
11
14
  from .paths.owners import DeployPathOwner
12
15
  from .paths.paths import DeployPath
13
16
  from .specs import DeployAppSpec
17
+ from .tags import DeployAppRev
14
18
  from .tags import DeployTagMap
15
19
  from .types import DeployHome
16
20
  from .venvs import DeployVenvManager
@@ -20,39 +24,31 @@ class DeployAppManager(DeployPathOwner):
20
24
  def __init__(
21
25
  self,
22
26
  *,
23
- conf: DeployConfManager,
24
27
  git: DeployGitManager,
25
28
  venvs: DeployVenvManager,
29
+ conf: DeployConfManager,
30
+
31
+ msh: ObjMarshalerManager,
26
32
  ) -> None:
27
33
  super().__init__()
28
34
 
29
- self._conf = conf
30
35
  self._git = git
31
36
  self._venvs = venvs
37
+ self._conf = conf
32
38
 
33
- #
34
-
35
- _APP_DIR_STR = 'apps/@app/@time--@app-rev--@app-key/'
36
- _APP_DIR = DeployPath.parse(_APP_DIR_STR)
39
+ self._msh = msh
37
40
 
38
- _DEPLOY_DIR_STR = 'deploys/@time--@deploy-key/'
39
- _DEPLOY_DIR = DeployPath.parse(_DEPLOY_DIR_STR)
41
+ #
40
42
 
41
- _APP_DEPLOY_LINK = DeployPath.parse(f'{_DEPLOY_DIR_STR}apps/@app')
42
- _CONF_DEPLOY_DIR = DeployPath.parse(f'{_DEPLOY_DIR_STR}conf/@conf/')
43
+ APP_DIR = DeployPath.parse('apps/@app/@time--@app-rev--@app-key/')
43
44
 
44
45
  @cached_nullary
45
46
  def get_owned_deploy_paths(self) -> ta.AbstractSet[DeployPath]:
46
47
  return {
47
- self._APP_DIR,
48
-
49
- self._DEPLOY_DIR,
50
-
51
- self._APP_DEPLOY_LINK,
52
- self._CONF_DEPLOY_DIR,
48
+ self.APP_DIR,
53
49
 
54
50
  *[
55
- DeployPath.parse(f'{self._APP_DIR_STR}{sfx}/')
51
+ DeployPath.parse(f'{self.APP_DIR}{sfx}/')
56
52
  for sfx in [
57
53
  'conf',
58
54
  'git',
@@ -63,115 +59,140 @@ class DeployAppManager(DeployPathOwner):
63
59
 
64
60
  #
65
61
 
62
+ def _make_tags(self, spec: DeployAppSpec) -> DeployTagMap:
63
+ return DeployTagMap(
64
+ spec.app,
65
+ spec.key(),
66
+ DeployAppRev(spec.git.rev),
67
+ )
68
+
69
+ #
70
+
71
+ @dc.dataclass(frozen=True)
72
+ class PreparedApp:
73
+ spec: DeployAppSpec
74
+ tags: DeployTagMap
75
+
76
+ dir: str
77
+
78
+ git_dir: ta.Optional[str] = None
79
+ venv_dir: ta.Optional[str] = None
80
+ conf_dir: ta.Optional[str] = None
81
+
66
82
  async def prepare_app(
67
83
  self,
68
84
  spec: DeployAppSpec,
69
85
  home: DeployHome,
70
86
  tags: DeployTagMap,
71
- ) -> None:
72
- check.non_empty_str(home)
87
+ ) -> PreparedApp:
88
+ spec_json = json_dumps_pretty(self._msh.marshal_obj(spec))
73
89
 
74
- def build_path(pth: DeployPath) -> str:
75
- return os.path.join(home, pth.render(tags))
90
+ #
76
91
 
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)
92
+ app_tags = tags.add(*self._make_tags(spec))
80
93
 
81
94
  #
82
95
 
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
- )
96
+ check.non_empty_str(home)
94
97
 
95
- #
98
+ app_dir = os.path.join(home, self.APP_DIR.render(app_tags))
96
99
 
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
- )
100
+ os.makedirs(app_dir, exist_ok=True)
104
101
 
105
102
  #
106
103
 
107
- deploy_conf_dir = os.path.join(deploy_dir, 'conf')
108
- os.makedirs(deploy_conf_dir, exist_ok=True)
104
+ rkw: ta.Dict[str, ta.Any] = dict(
105
+ spec=spec,
106
+ tags=app_tags,
109
107
 
110
- #
108
+ dir=app_dir,
109
+ )
111
110
 
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
111
  #
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
- # )
112
+
113
+ spec_file = os.path.join(app_dir, 'spec.json')
114
+ with open(spec_file, 'w') as f: # noqa
115
+ f.write(spec_json)
143
116
 
144
117
  #
145
118
 
146
- app_git_dir = os.path.join(app_dir, 'git')
119
+ git_dir = os.path.join(app_dir, 'git')
120
+ rkw.update(git_dir=git_dir)
147
121
  await self._git.checkout(
148
122
  spec.git,
149
123
  home,
150
- app_git_dir,
124
+ git_dir,
151
125
  )
152
126
 
153
127
  #
154
128
 
155
129
  if spec.venv is not None:
156
- app_venv_dir = os.path.join(app_dir, 'venv')
130
+ venv_dir = os.path.join(app_dir, 'venv')
131
+ rkw.update(venv_dir=venv_dir)
157
132
  await self._venvs.setup_venv(
158
133
  spec.venv,
159
- home,
160
- app_git_dir,
161
- app_venv_dir,
134
+ git_dir,
135
+ venv_dir,
162
136
  )
163
137
 
164
138
  #
165
139
 
166
140
  if spec.conf is not None:
167
- app_conf_dir = os.path.join(app_dir, 'conf')
141
+ conf_dir = os.path.join(app_dir, 'conf')
142
+ rkw.update(conf_dir=conf_dir)
168
143
  await self._conf.write_app_conf(
169
144
  spec.conf,
170
- tags,
171
- app_conf_dir,
172
- deploy_conf_dir,
145
+ conf_dir,
173
146
  )
174
147
 
175
148
  #
176
149
 
177
- os.replace(deploying_link, current_link)
150
+ return DeployAppManager.PreparedApp(**rkw)
151
+
152
+ async def prepare_app_link(
153
+ self,
154
+ tags: DeployTagMap,
155
+ app_dir: str,
156
+ ) -> PreparedApp:
157
+ spec_file = os.path.join(app_dir, 'spec.json')
158
+ with open(spec_file) as f: # noqa
159
+ spec_json = f.read()
160
+
161
+ spec: DeployAppSpec = self._msh.unmarshal_obj(json.loads(spec_json), DeployAppSpec)
162
+
163
+ #
164
+
165
+ app_tags = tags.add(*self._make_tags(spec))
166
+
167
+ #
168
+
169
+ rkw: ta.Dict[str, ta.Any] = dict(
170
+ spec=spec,
171
+ tags=app_tags,
172
+
173
+ dir=app_dir,
174
+ )
175
+
176
+ #
177
+
178
+ git_dir = os.path.join(app_dir, 'git')
179
+ check.state(os.path.isdir(git_dir))
180
+ rkw.update(git_dir=git_dir)
181
+
182
+ #
183
+
184
+ if spec.venv is not None:
185
+ venv_dir = os.path.join(app_dir, 'venv')
186
+ check.state(os.path.isdir(venv_dir))
187
+ rkw.update(venv_dir=venv_dir)
188
+
189
+ #
190
+
191
+ if spec.conf is not None:
192
+ conf_dir = os.path.join(app_dir, 'conf')
193
+ check.state(os.path.isdir(conf_dir))
194
+ rkw.update(conf_dir=conf_dir)
195
+
196
+ #
197
+
198
+ 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
 
@@ -16,6 +16,8 @@ TODO:
16
16
  - some things (venvs) cannot be moved, thus the /deploy/venvs dir
17
17
  - ** ensure (enforce) equivalent relpath nesting
18
18
  """
19
+ import collections.abc
20
+ import functools
19
21
  import os.path
20
22
  import typing as ta
21
23
 
@@ -24,6 +26,8 @@ from omlish.lite.json import json_dumps_pretty
24
26
  from omlish.lite.strings import strip_with_newline
25
27
  from omlish.os.paths import is_path_in_dir
26
28
  from omlish.os.paths import relative_symlink
29
+ from omserv.nginx.configs import NginxConfigItems
30
+ from omserv.nginx.configs import render_nginx_config_str
27
31
 
28
32
  from ....configs import render_ini_config
29
33
  from ..paths.paths import DeployPath
@@ -37,19 +41,70 @@ from .specs import DeployAppConfLink
37
41
  from .specs import DeployAppConfSpec
38
42
  from .specs import IniDeployAppConfContent
39
43
  from .specs import JsonDeployAppConfContent
44
+ from .specs import NginxDeployAppConfContent
40
45
  from .specs import RawDeployAppConfContent
41
46
 
42
47
 
48
+ T = ta.TypeVar('T')
49
+
50
+
51
+ ##
52
+
53
+
43
54
  class DeployConfManager:
44
- def _render_app_conf_content(self, ac: DeployAppConfContent) -> str:
55
+ def _process_conf_content(
56
+ self,
57
+ content: T,
58
+ *,
59
+ str_processor: ta.Optional[ta.Callable[[str], str]] = None,
60
+ ) -> T:
61
+ def rec(o):
62
+ if isinstance(o, str):
63
+ if str_processor is not None:
64
+ return type(o)(str_processor(o))
65
+
66
+ elif isinstance(o, collections.abc.Mapping):
67
+ return type(o)([ # type: ignore
68
+ (rec(k), rec(v))
69
+ for k, v in o.items()
70
+ ])
71
+
72
+ elif isinstance(o, collections.abc.Iterable):
73
+ return type(o)([ # type: ignore
74
+ rec(e) for e in o
75
+ ])
76
+
77
+ return o
78
+
79
+ return rec(content)
80
+
81
+ #
82
+
83
+ def _render_app_conf_content(
84
+ self,
85
+ ac: DeployAppConfContent,
86
+ *,
87
+ str_processor: ta.Optional[ta.Callable[[str], str]] = None,
88
+ ) -> str:
89
+ pcc = functools.partial(
90
+ self._process_conf_content,
91
+ str_processor=str_processor,
92
+ )
93
+
45
94
  if isinstance(ac, RawDeployAppConfContent):
46
- return ac.body
95
+ return pcc(ac.body)
47
96
 
48
97
  elif isinstance(ac, JsonDeployAppConfContent):
49
- return strip_with_newline(json_dumps_pretty(ac.obj))
98
+ json_obj = pcc(ac.obj)
99
+ return strip_with_newline(json_dumps_pretty(json_obj))
50
100
 
51
101
  elif isinstance(ac, IniDeployAppConfContent):
52
- return strip_with_newline(render_ini_config(ac.sections))
102
+ ini_sections = pcc(ac.sections)
103
+ return strip_with_newline(render_ini_config(ini_sections))
104
+
105
+ elif isinstance(ac, NginxDeployAppConfContent):
106
+ nginx_items = NginxConfigItems.of(pcc(ac.items))
107
+ return strip_with_newline(render_nginx_config_str(nginx_items))
53
108
 
54
109
  else:
55
110
  raise TypeError(ac)
@@ -58,17 +113,37 @@ class DeployConfManager:
58
113
  self,
59
114
  acf: DeployAppConfFile,
60
115
  app_conf_dir: str,
116
+ *,
117
+ str_processor: ta.Optional[ta.Callable[[str], str]] = None,
61
118
  ) -> None:
62
119
  conf_file = os.path.join(app_conf_dir, acf.path)
63
120
  check.arg(is_path_in_dir(app_conf_dir, conf_file))
64
121
 
65
- body = self._render_app_conf_content(acf.content)
122
+ body = self._render_app_conf_content(
123
+ acf.content,
124
+ str_processor=str_processor,
125
+ )
66
126
 
67
127
  os.makedirs(os.path.dirname(conf_file), exist_ok=True)
68
128
 
69
129
  with open(conf_file, 'w') as f: # noqa
70
130
  f.write(body)
71
131
 
132
+ async def write_app_conf(
133
+ self,
134
+ spec: DeployAppConfSpec,
135
+ app_conf_dir: str,
136
+ ) -> None:
137
+ def process_str(s: str) -> str:
138
+ return s
139
+
140
+ for acf in spec.files or []:
141
+ await self._write_app_conf_file(
142
+ acf,
143
+ app_conf_dir,
144
+ str_processor=process_str,
145
+ )
146
+
72
147
  #
73
148
 
74
149
  class _ComputedConfLink(ta.NamedTuple):
@@ -178,23 +253,13 @@ class DeployConfManager:
178
253
  make_dirs=True,
179
254
  )
180
255
 
181
- #
182
-
183
- async def write_app_conf(
256
+ async def link_app_conf(
184
257
  self,
185
258
  spec: DeployAppConfSpec,
186
259
  tags: DeployTagMap,
187
260
  app_conf_dir: str,
188
261
  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
-
262
+ ):
198
263
  for link in spec.links or []:
199
264
  await self._make_app_conf_link(
200
265
  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