ominfra 0.0.0.dev189__py3-none-any.whl → 0.0.0.dev191__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
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