magic-pocket-cli 0.9.0__tar.gz → 0.10.0__tar.gz

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.
Files changed (67) hide show
  1. {magic_pocket_cli-0.9.0 → magic_pocket_cli-0.10.0}/PKG-INFO +2 -2
  2. {magic_pocket_cli-0.9.0 → magic_pocket_cli-0.10.0}/pocket_cli/cli/neon_cli.py +31 -0
  3. {magic_pocket_cli-0.9.0 → magic_pocket_cli-0.10.0}/pocket_cli/cli/tidb_cli.py +37 -0
  4. magic_pocket_cli-0.10.0/pocket_cli/cli/url_helper.py +81 -0
  5. {magic_pocket_cli-0.9.0 → magic_pocket_cli-0.10.0}/pocket_cli/mediator.py +33 -0
  6. {magic_pocket_cli-0.9.0 → magic_pocket_cli-0.10.0}/pyproject.toml +2 -2
  7. {magic_pocket_cli-0.9.0 → magic_pocket_cli-0.10.0}/.gitignore +0 -0
  8. {magic_pocket_cli-0.9.0 → magic_pocket_cli-0.10.0}/pocket_cli/__init__.py +0 -0
  9. {magic_pocket_cli-0.9.0 → magic_pocket_cli-0.10.0}/pocket_cli/cli/__init__.py +0 -0
  10. {magic_pocket_cli-0.9.0 → magic_pocket_cli-0.10.0}/pocket_cli/cli/aws_auth.py +0 -0
  11. {magic_pocket_cli-0.9.0 → magic_pocket_cli-0.10.0}/pocket_cli/cli/awscontainer_cli.py +0 -0
  12. {magic_pocket_cli-0.9.0 → magic_pocket_cli-0.10.0}/pocket_cli/cli/cloudfront_cli.py +0 -0
  13. {magic_pocket_cli-0.9.0 → magic_pocket_cli-0.10.0}/pocket_cli/cli/cloudfront_keys_cli.py +0 -0
  14. {magic_pocket_cli-0.9.0 → magic_pocket_cli-0.10.0}/pocket_cli/cli/cloudfront_waf_cli.py +0 -0
  15. {magic_pocket_cli-0.9.0 → magic_pocket_cli-0.10.0}/pocket_cli/cli/deploy_cli.py +0 -0
  16. {magic_pocket_cli-0.9.0 → magic_pocket_cli-0.10.0}/pocket_cli/cli/destroy_cli.py +0 -0
  17. {magic_pocket_cli-0.9.0 → magic_pocket_cli-0.10.0}/pocket_cli/cli/dsql_cli.py +0 -0
  18. {magic_pocket_cli-0.9.0 → magic_pocket_cli-0.10.0}/pocket_cli/cli/main_cli.py +0 -0
  19. {magic_pocket_cli-0.9.0 → magic_pocket_cli-0.10.0}/pocket_cli/cli/migrate_cli.py +0 -0
  20. {magic_pocket_cli-0.9.0 → magic_pocket_cli-0.10.0}/pocket_cli/cli/permissions_cli.py +0 -0
  21. {magic_pocket_cli-0.9.0 → magic_pocket_cli-0.10.0}/pocket_cli/cli/rds_cli.py +0 -0
  22. {magic_pocket_cli-0.9.0 → magic_pocket_cli-0.10.0}/pocket_cli/cli/runtime_config_cli.py +0 -0
  23. {magic_pocket_cli-0.9.0 → magic_pocket_cli-0.10.0}/pocket_cli/cli/s3_cli.py +0 -0
  24. {magic_pocket_cli-0.9.0 → magic_pocket_cli-0.10.0}/pocket_cli/cli/status_cli.py +0 -0
  25. {magic_pocket_cli-0.9.0 → magic_pocket_cli-0.10.0}/pocket_cli/cli/store_url_helper.py +0 -0
  26. {magic_pocket_cli-0.9.0 → magic_pocket_cli-0.10.0}/pocket_cli/cli/upstash_cli.py +0 -0
  27. {magic_pocket_cli-0.9.0 → magic_pocket_cli-0.10.0}/pocket_cli/cli/vpc_cli.py +0 -0
  28. {magic_pocket_cli-0.9.0 → magic_pocket_cli-0.10.0}/pocket_cli/cli/waf_cli.py +0 -0
  29. {magic_pocket_cli-0.9.0 → magic_pocket_cli-0.10.0}/pocket_cli/django_cli.py +0 -0
  30. {magic_pocket_cli-0.9.0 → magic_pocket_cli-0.10.0}/pocket_cli/resources/__init__.py +0 -0
  31. {magic_pocket_cli-0.9.0 → magic_pocket_cli-0.10.0}/pocket_cli/resources/aws/__init__.py +0 -0
  32. {magic_pocket_cli-0.9.0 → magic_pocket_cli-0.10.0}/pocket_cli/resources/aws/builders/__init__.py +0 -0
  33. {magic_pocket_cli-0.9.0 → magic_pocket_cli-0.10.0}/pocket_cli/resources/aws/builders/codebuild.py +0 -0
  34. {magic_pocket_cli-0.9.0 → magic_pocket_cli-0.10.0}/pocket_cli/resources/aws/builders/depot.py +0 -0
  35. {magic_pocket_cli-0.9.0 → magic_pocket_cli-0.10.0}/pocket_cli/resources/aws/builders/docker.py +0 -0
  36. {magic_pocket_cli-0.9.0 → magic_pocket_cli-0.10.0}/pocket_cli/resources/aws/builders/dockerignore.py +0 -0
  37. {magic_pocket_cli-0.9.0 → magic_pocket_cli-0.10.0}/pocket_cli/resources/aws/cloudformation.py +0 -0
  38. {magic_pocket_cli-0.9.0 → magic_pocket_cli-0.10.0}/pocket_cli/resources/aws/ecr.py +0 -0
  39. {magic_pocket_cli-0.9.0 → magic_pocket_cli-0.10.0}/pocket_cli/resources/aws/efs.py +0 -0
  40. {magic_pocket_cli-0.9.0 → magic_pocket_cli-0.10.0}/pocket_cli/resources/aws/lambdahandler.py +0 -0
  41. {magic_pocket_cli-0.9.0 → magic_pocket_cli-0.10.0}/pocket_cli/resources/aws/s3_utils.py +0 -0
  42. {magic_pocket_cli-0.9.0 → magic_pocket_cli-0.10.0}/pocket_cli/resources/aws/state.py +0 -0
  43. {magic_pocket_cli-0.9.0 → magic_pocket_cli-0.10.0}/pocket_cli/resources/awscontainer.py +0 -0
  44. {magic_pocket_cli-0.9.0 → magic_pocket_cli-0.10.0}/pocket_cli/resources/cloudfront.py +0 -0
  45. {magic_pocket_cli-0.9.0 → magic_pocket_cli-0.10.0}/pocket_cli/resources/cloudfront_acm.py +0 -0
  46. {magic_pocket_cli-0.9.0 → magic_pocket_cli-0.10.0}/pocket_cli/resources/cloudfront_keys.py +0 -0
  47. {magic_pocket_cli-0.9.0 → magic_pocket_cli-0.10.0}/pocket_cli/resources/cloudfront_waf.py +0 -0
  48. {magic_pocket_cli-0.9.0 → magic_pocket_cli-0.10.0}/pocket_cli/resources/dsql.py +0 -0
  49. {magic_pocket_cli-0.9.0 → magic_pocket_cli-0.10.0}/pocket_cli/resources/neon.py +0 -0
  50. {magic_pocket_cli-0.9.0 → magic_pocket_cli-0.10.0}/pocket_cli/resources/rds.py +0 -0
  51. {magic_pocket_cli-0.9.0 → magic_pocket_cli-0.10.0}/pocket_cli/resources/s3.py +0 -0
  52. {magic_pocket_cli-0.9.0 → magic_pocket_cli-0.10.0}/pocket_cli/resources/tidb.py +0 -0
  53. {magic_pocket_cli-0.9.0 → magic_pocket_cli-0.10.0}/pocket_cli/resources/upstash.py +0 -0
  54. {magic_pocket_cli-0.9.0 → magic_pocket_cli-0.10.0}/pocket_cli/resources/vpc.py +0 -0
  55. {magic_pocket_cli-0.9.0 → magic_pocket_cli-0.10.0}/pocket_cli/templates/cloudformation/awscontainer.yaml +0 -0
  56. {magic_pocket_cli-0.9.0 → magic_pocket_cli-0.10.0}/pocket_cli/templates/cloudformation/cf_function_api_host.js +0 -0
  57. {magic_pocket_cli-0.9.0 → magic_pocket_cli-0.10.0}/pocket_cli/templates/cloudformation/cf_function_spa_auth.js +0 -0
  58. {magic_pocket_cli-0.9.0 → magic_pocket_cli-0.10.0}/pocket_cli/templates/cloudformation/cf_function_spa_fallback.js +0 -0
  59. {magic_pocket_cli-0.9.0 → magic_pocket_cli-0.10.0}/pocket_cli/templates/cloudformation/cloudfront.yaml +0 -0
  60. {magic_pocket_cli-0.9.0 → magic_pocket_cli-0.10.0}/pocket_cli/templates/cloudformation/cloudfront_acm.yaml +0 -0
  61. {magic_pocket_cli-0.9.0 → magic_pocket_cli-0.10.0}/pocket_cli/templates/cloudformation/cloudfront_keys.yaml +0 -0
  62. {magic_pocket_cli-0.9.0 → magic_pocket_cli-0.10.0}/pocket_cli/templates/cloudformation/cloudfront_waf.yaml +0 -0
  63. {magic_pocket_cli-0.9.0 → magic_pocket_cli-0.10.0}/pocket_cli/templates/cloudformation/vpc.yaml +0 -0
  64. {magic_pocket_cli-0.9.0 → magic_pocket_cli-0.10.0}/pocket_cli/templates/init/django-dotenv.env +0 -0
  65. {magic_pocket_cli-0.9.0 → magic_pocket_cli-0.10.0}/pocket_cli/templates/init/django-settings.py +0 -0
  66. {magic_pocket_cli-0.9.0 → magic_pocket_cli-0.10.0}/pocket_cli/templates/init/pocket.Dockerfile +0 -0
  67. {magic_pocket_cli-0.9.0 → magic_pocket_cli-0.10.0}/pocket_cli/templates/init/pocket_simple.toml +0 -0
@@ -1,13 +1,13 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: magic-pocket-cli
3
- Version: 0.9.0
3
+ Version: 0.10.0
4
4
  Summary: CLI and deploy tools for magic-pocket.
5
5
  Requires-Python: >=3.10
6
6
  Requires-Dist: awscrt>=0.19.0
7
7
  Requires-Dist: click>=8.1.7
8
8
  Requires-Dist: deepdiff>=6.7.1
9
9
  Requires-Dist: jinja2>=3.1.3
10
- Requires-Dist: magic-pocket>=0.9.0
10
+ Requires-Dist: magic-pocket>=0.10.0
11
11
  Requires-Dist: pathspec>=1.0.4
12
12
  Requires-Dist: python-on-whales>=0.68.0
13
13
  Requires-Dist: pyyaml>=6.0.1
@@ -5,6 +5,7 @@ import click
5
5
  from pocket.context import Context
6
6
  from pocket.utils import echo
7
7
  from pocket_cli.cli.store_url_helper import run_store_url
8
+ from pocket_cli.cli.url_helper import run_get_url
8
9
  from pocket_cli.resources.neon import Neon
9
10
 
10
11
 
@@ -100,6 +101,36 @@ def store_url(stage, key, force):
100
101
  )
101
102
 
102
103
 
104
+ @neon.command()
105
+ @click.option("--stage", envvar="POCKET_DEPLOY_STAGE", prompt=True)
106
+ @click.option(
107
+ "--live",
108
+ is_flag=True,
109
+ help="stored を見ず provider API で live 算出する (Neon は reveal 方式で冪等)",
110
+ )
111
+ def url(stage, live):
112
+ """接続 URL を stdout に出力する (default: stored-first / --live: provider API)。
113
+
114
+ 移行ツール等が `$(pocket resource neon url --stage <s>)` で食える純 URL のみを
115
+ stdout に出す (診断は stderr)。dual-declaration 下では source(Neon) の解決に使う。
116
+ """
117
+
118
+ def live_url(context):
119
+ if not context.neon:
120
+ raise click.ClickException(
121
+ "neon が pocket.toml に宣言されていません (live 算出不可)"
122
+ )
123
+ return Neon(context.neon).database_url
124
+
125
+ run_get_url(
126
+ stage=stage,
127
+ secret_type="neon_database_url", # noqa: S106 (secret type 名であって credential ではない)
128
+ db_label="Neon",
129
+ live_url=live_url,
130
+ live=live,
131
+ )
132
+
133
+
103
134
  @neon.command()
104
135
  @click.option("--stage", envvar="POCKET_DEPLOY_STAGE", prompt=True)
105
136
  def status(stage):
@@ -5,6 +5,7 @@ import click
5
5
  from pocket.context import Context
6
6
  from pocket.utils import echo
7
7
  from pocket_cli.cli.store_url_helper import run_store_url
8
+ from pocket_cli.cli.url_helper import run_get_url
8
9
  from pocket_cli.resources.tidb import TiDb
9
10
 
10
11
 
@@ -84,6 +85,42 @@ def store_url(stage, key, force):
84
85
  )
85
86
 
86
87
 
88
+ @tidb.command()
89
+ @click.option("--stage", envvar="POCKET_DEPLOY_STAGE", prompt=True)
90
+ @click.option(
91
+ "--live",
92
+ is_flag=True,
93
+ help=(
94
+ "stored user secret を見ず provider API で live 算出する。"
95
+ "注意: TiDB は reveal API が無いため root password を rotate する "
96
+ "(consumer の redeploy が前提)"
97
+ ),
98
+ )
99
+ def url(stage, live):
100
+ """接続 URL を stdout に出力する (default: stored-first / --live: provider API)。
101
+
102
+ 移行ツール等が `$(pocket resource tidb url --stage <s>)` で食える純 URL のみを
103
+ stdout に出す (診断は stderr)。dual-declaration 下では target(TiDB) の解決に使う。
104
+ default の stored-first は副作用が無く consumer が使う URL と一致する。--live は
105
+ root password を rotate する点に注意。
106
+ """
107
+
108
+ def live_url(context):
109
+ if not context.tidb:
110
+ raise click.ClickException(
111
+ "tidb が pocket.toml に宣言されていません (live 算出不可)"
112
+ )
113
+ return TiDb(context.tidb).database_url
114
+
115
+ run_get_url(
116
+ stage=stage,
117
+ secret_type="tidb_database_url", # noqa: S106 (secret type 名であって credential ではない)
118
+ db_label="TiDB",
119
+ live_url=live_url,
120
+ live=live,
121
+ )
122
+
123
+
87
124
  @tidb.command()
88
125
  @click.option("--stage", envvar="POCKET_DEPLOY_STAGE", prompt=True)
89
126
  def status(stage):
@@ -0,0 +1,81 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import Callable
4
+
5
+ import click
6
+
7
+ from pocket.context import Context
8
+ from pocket_cli.mediator import Mediator
9
+
10
+
11
+ def run_get_url(
12
+ *,
13
+ stage: str,
14
+ secret_type: str,
15
+ db_label: str,
16
+ live_url: Callable[[Context], str],
17
+ live: bool = False,
18
+ ) -> None:
19
+ """DB 接続 URL を stdout に純テキストで出力する (`pocket resource <db> url`)。
20
+
21
+ 移行ツール等が ``$(pocket resource neon url --stage prod)`` で食える「URL 文字列
22
+ だけ」を stdout に出す。診断・警告はすべて stderr に流し、stdout を汚さない。
23
+
24
+ 解決方式:
25
+
26
+ - default (stored-first): ``type=<secret_type>`` の stored user secret を store から
27
+ 読む。副作用が無く、consumer (app) が実際に使う URL と一致する。未 provision なら
28
+ provider API での live 算出に fallback する。
29
+ - ``--live``: stored を見ず必ず provider API で live 算出する。常に最新だが、reveal
30
+ API を持たない backend (TiDB 等) では **root password を rotate する** 点に注意
31
+ (consumer の redeploy が前提)。
32
+
33
+ dual-declaration (移行中に ``[neon]`` と ``[tidb]`` を併記) 下でも、resource ごとに
34
+ neon / tidb を呼び分ければ source/target 双方を解決できる。
35
+ """
36
+ context = Context.from_toml(stage=stage)
37
+
38
+ if live:
39
+ # --live は明示要求。失敗時は例外をそのまま伝播させる (fallback しない)。
40
+ click.echo(live_url(context))
41
+ return
42
+
43
+ stored = _read_stored_url(context, secret_type)
44
+ if stored is not None:
45
+ click.echo(stored)
46
+ return
47
+
48
+ click.echo(
49
+ "%s: stored user secret が未 provision のため provider API で live 算出します"
50
+ " (reveal API の無い backend では root password が rotate されます)。"
51
+ % db_label,
52
+ err=True,
53
+ )
54
+ try:
55
+ url = live_url(context)
56
+ except Exception as e:
57
+ raise click.ClickException(
58
+ "%s の接続 URL を解決できませんでした。stored secret が無く live 算出も"
59
+ "失敗 (%s)。[<db>] 宣言 + provider 資格情報、または store-url を"
60
+ "確認してください。" % (db_label, e)
61
+ ) from None
62
+ click.echo(url)
63
+
64
+
65
+ def _read_stored_url(context: Context, secret_type: str) -> str | None:
66
+ """``type=<secret_type>`` の stored user secret を 1 件だけ特定して値を読む。
67
+
68
+ 宣言が 0 件なら None (→ live fallback)、複数なら曖昧として ClickException。
69
+ """
70
+ sc = context.awscontainer.secrets if context.awscontainer else None
71
+ if sc is None:
72
+ return None
73
+ specs = [spec for spec in sc.user.values() if spec.type == secret_type]
74
+ if not specs:
75
+ return None
76
+ if len(specs) > 1:
77
+ raise click.ClickException(
78
+ "type=%s の stored user secret が複数宣言されているため曖昧です。"
79
+ % secret_type
80
+ )
81
+ return Mediator(context).read_user_secret(specs[0])
@@ -181,6 +181,39 @@ class Mediator:
181
181
  else:
182
182
  raise
183
183
 
184
+ def read_user_secret(self, spec) -> str | None:
185
+ """stored mode user secret (spec.name) の値を読む。未 provision なら None。
186
+
187
+ store_user_secret / _stored_secret_exists と対称の読み取り。
188
+ `pocket <db> url` の stored-first 解決に使う。管理 API は叩かない。
189
+ """
190
+ import boto3
191
+ from botocore.exceptions import ClientError
192
+
193
+ if (
194
+ self.context.awscontainer is None
195
+ or self.context.awscontainer.secrets is None
196
+ or spec.name is None
197
+ ):
198
+ return None
199
+ sc = self.context.awscontainer.secrets
200
+ store = spec.store or sc.store
201
+ try:
202
+ if store == "ssm":
203
+ res = boto3.client("ssm", region_name=sc.region).get_parameter(
204
+ Name=spec.name, WithDecryption=True
205
+ )
206
+ return res["Parameter"]["Value"]
207
+ res = boto3.client(
208
+ "secretsmanager", region_name=sc.region
209
+ ).get_secret_value(SecretId=spec.name)
210
+ return res["SecretString"]
211
+ except ClientError as e:
212
+ code = e.response.get("Error", {}).get("Code", "")
213
+ if code in ("ParameterNotFound", "ResourceNotFoundException"):
214
+ return None
215
+ raise
216
+
184
217
  def _cleanup_orphaned_secrets(self):
185
218
  """SSM/SM にあるが managed 定義にないシークレットを削除する"""
186
219
  if self.context.awscontainer is None:
@@ -1,10 +1,10 @@
1
1
  [project]
2
2
  name = "magic-pocket-cli"
3
- version = "0.9.0"
3
+ version = "0.10.0"
4
4
  description = "CLI and deploy tools for magic-pocket."
5
5
  requires-python = ">=3.10"
6
6
  dependencies = [
7
- "magic-pocket>=0.9.0",
7
+ "magic-pocket>=0.10.0",
8
8
  "click>=8.1.7",
9
9
  "jinja2>=3.1.3",
10
10
  "python-on-whales>=0.68.0",