magic-pocket-cli 0.28.0__tar.gz → 0.28.2__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 (79) hide show
  1. {magic_pocket_cli-0.28.0 → magic_pocket_cli-0.28.2}/PKG-INFO +2 -2
  2. {magic_pocket_cli-0.28.0 → magic_pocket_cli-0.28.2}/pocket_cli/cli/deploy_cli.py +3 -1
  3. {magic_pocket_cli-0.28.0 → magic_pocket_cli-0.28.2}/pocket_cli/resources/backup.py +43 -10
  4. {magic_pocket_cli-0.28.0 → magic_pocket_cli-0.28.2}/pocket_cli/resources/cloudfront.py +17 -0
  5. {magic_pocket_cli-0.28.0 → magic_pocket_cli-0.28.2}/pyproject.toml +2 -2
  6. {magic_pocket_cli-0.28.0 → magic_pocket_cli-0.28.2}/.gitignore +0 -0
  7. {magic_pocket_cli-0.28.0 → magic_pocket_cli-0.28.2}/pocket_cli/__init__.py +0 -0
  8. {magic_pocket_cli-0.28.0 → magic_pocket_cli-0.28.2}/pocket_cli/cli/__init__.py +0 -0
  9. {magic_pocket_cli-0.28.0 → magic_pocket_cli-0.28.2}/pocket_cli/cli/aws_auth.py +0 -0
  10. {magic_pocket_cli-0.28.0 → magic_pocket_cli-0.28.2}/pocket_cli/cli/awscontainer_cli.py +0 -0
  11. {magic_pocket_cli-0.28.0 → magic_pocket_cli-0.28.2}/pocket_cli/cli/backup_cli.py +0 -0
  12. {magic_pocket_cli-0.28.0 → magic_pocket_cli-0.28.2}/pocket_cli/cli/cloudfront_cli.py +0 -0
  13. {magic_pocket_cli-0.28.0 → magic_pocket_cli-0.28.2}/pocket_cli/cli/cloudfront_keys_cli.py +0 -0
  14. {magic_pocket_cli-0.28.0 → magic_pocket_cli-0.28.2}/pocket_cli/cli/cloudfront_waf_cli.py +0 -0
  15. {magic_pocket_cli-0.28.0 → magic_pocket_cli-0.28.2}/pocket_cli/cli/destroy_cli.py +0 -0
  16. {magic_pocket_cli-0.28.0 → magic_pocket_cli-0.28.2}/pocket_cli/cli/dsql_cli.py +0 -0
  17. {magic_pocket_cli-0.28.0 → magic_pocket_cli-0.28.2}/pocket_cli/cli/image_cli.py +0 -0
  18. {magic_pocket_cli-0.28.0 → magic_pocket_cli-0.28.2}/pocket_cli/cli/interaction.py +0 -0
  19. {magic_pocket_cli-0.28.0 → magic_pocket_cli-0.28.2}/pocket_cli/cli/main_cli.py +0 -0
  20. {magic_pocket_cli-0.28.0 → magic_pocket_cli-0.28.2}/pocket_cli/cli/migrate_cli.py +0 -0
  21. {magic_pocket_cli-0.28.0 → magic_pocket_cli-0.28.2}/pocket_cli/cli/neon_cli.py +0 -0
  22. {magic_pocket_cli-0.28.0 → magic_pocket_cli-0.28.2}/pocket_cli/cli/permissions_cli.py +0 -0
  23. {magic_pocket_cli-0.28.0 → magic_pocket_cli-0.28.2}/pocket_cli/cli/rds_cli.py +0 -0
  24. {magic_pocket_cli-0.28.0 → magic_pocket_cli-0.28.2}/pocket_cli/cli/removed_flags.py +0 -0
  25. {magic_pocket_cli-0.28.0 → magic_pocket_cli-0.28.2}/pocket_cli/cli/resource_helper.py +0 -0
  26. {magic_pocket_cli-0.28.0 → magic_pocket_cli-0.28.2}/pocket_cli/cli/runtime_config_cli.py +0 -0
  27. {magic_pocket_cli-0.28.0 → magic_pocket_cli-0.28.2}/pocket_cli/cli/s3_cli.py +0 -0
  28. {magic_pocket_cli-0.28.0 → magic_pocket_cli-0.28.2}/pocket_cli/cli/status_cli.py +0 -0
  29. {magic_pocket_cli-0.28.0 → magic_pocket_cli-0.28.2}/pocket_cli/cli/store_url_helper.py +0 -0
  30. {magic_pocket_cli-0.28.0 → magic_pocket_cli-0.28.2}/pocket_cli/cli/tidb_cli.py +0 -0
  31. {magic_pocket_cli-0.28.0 → magic_pocket_cli-0.28.2}/pocket_cli/cli/upstash_cli.py +0 -0
  32. {magic_pocket_cli-0.28.0 → magic_pocket_cli-0.28.2}/pocket_cli/cli/url_helper.py +0 -0
  33. {magic_pocket_cli-0.28.0 → magic_pocket_cli-0.28.2}/pocket_cli/cli/vpc_cli.py +0 -0
  34. {magic_pocket_cli-0.28.0 → magic_pocket_cli-0.28.2}/pocket_cli/cli/waf_cli.py +0 -0
  35. {magic_pocket_cli-0.28.0 → magic_pocket_cli-0.28.2}/pocket_cli/django_cli.py +0 -0
  36. {magic_pocket_cli-0.28.0 → magic_pocket_cli-0.28.2}/pocket_cli/mediator.py +0 -0
  37. {magic_pocket_cli-0.28.0 → magic_pocket_cli-0.28.2}/pocket_cli/resources/__init__.py +0 -0
  38. {magic_pocket_cli-0.28.0 → magic_pocket_cli-0.28.2}/pocket_cli/resources/aws/__init__.py +0 -0
  39. {magic_pocket_cli-0.28.0 → magic_pocket_cli-0.28.2}/pocket_cli/resources/aws/backup_common.py +0 -0
  40. {magic_pocket_cli-0.28.0 → magic_pocket_cli-0.28.2}/pocket_cli/resources/aws/builders/__init__.py +0 -0
  41. {magic_pocket_cli-0.28.0 → magic_pocket_cli-0.28.2}/pocket_cli/resources/aws/builders/codebuild.py +0 -0
  42. {magic_pocket_cli-0.28.0 → magic_pocket_cli-0.28.2}/pocket_cli/resources/aws/builders/context_check.py +0 -0
  43. {magic_pocket_cli-0.28.0 → magic_pocket_cli-0.28.2}/pocket_cli/resources/aws/builders/depot.py +0 -0
  44. {magic_pocket_cli-0.28.0 → magic_pocket_cli-0.28.2}/pocket_cli/resources/aws/builders/docker.py +0 -0
  45. {magic_pocket_cli-0.28.0 → magic_pocket_cli-0.28.2}/pocket_cli/resources/aws/builders/dockerignore.py +0 -0
  46. {magic_pocket_cli-0.28.0 → magic_pocket_cli-0.28.2}/pocket_cli/resources/aws/cloudformation.py +0 -0
  47. {magic_pocket_cli-0.28.0 → magic_pocket_cli-0.28.2}/pocket_cli/resources/aws/ecr.py +0 -0
  48. {magic_pocket_cli-0.28.0 → magic_pocket_cli-0.28.2}/pocket_cli/resources/aws/efs.py +0 -0
  49. {magic_pocket_cli-0.28.0 → magic_pocket_cli-0.28.2}/pocket_cli/resources/aws/lambdahandler.py +0 -0
  50. {magic_pocket_cli-0.28.0 → magic_pocket_cli-0.28.2}/pocket_cli/resources/aws/poll.py +0 -0
  51. {magic_pocket_cli-0.28.0 → magic_pocket_cli-0.28.2}/pocket_cli/resources/aws/s3_utils.py +0 -0
  52. {magic_pocket_cli-0.28.0 → magic_pocket_cli-0.28.2}/pocket_cli/resources/aws/stack_backed.py +0 -0
  53. {magic_pocket_cli-0.28.0 → magic_pocket_cli-0.28.2}/pocket_cli/resources/aws/state.py +0 -0
  54. {magic_pocket_cli-0.28.0 → magic_pocket_cli-0.28.2}/pocket_cli/resources/awscontainer.py +0 -0
  55. {magic_pocket_cli-0.28.0 → magic_pocket_cli-0.28.2}/pocket_cli/resources/cloudfront_acm.py +0 -0
  56. {magic_pocket_cli-0.28.0 → magic_pocket_cli-0.28.2}/pocket_cli/resources/cloudfront_keys.py +0 -0
  57. {magic_pocket_cli-0.28.0 → magic_pocket_cli-0.28.2}/pocket_cli/resources/cloudfront_waf.py +0 -0
  58. {magic_pocket_cli-0.28.0 → magic_pocket_cli-0.28.2}/pocket_cli/resources/dsql.py +0 -0
  59. {magic_pocket_cli-0.28.0 → magic_pocket_cli-0.28.2}/pocket_cli/resources/neon.py +0 -0
  60. {magic_pocket_cli-0.28.0 → magic_pocket_cli-0.28.2}/pocket_cli/resources/rds.py +0 -0
  61. {magic_pocket_cli-0.28.0 → magic_pocket_cli-0.28.2}/pocket_cli/resources/s3.py +0 -0
  62. {magic_pocket_cli-0.28.0 → magic_pocket_cli-0.28.2}/pocket_cli/resources/tidb.py +0 -0
  63. {magic_pocket_cli-0.28.0 → magic_pocket_cli-0.28.2}/pocket_cli/resources/upstash.py +0 -0
  64. {magic_pocket_cli-0.28.0 → magic_pocket_cli-0.28.2}/pocket_cli/resources/vpc.py +0 -0
  65. {magic_pocket_cli-0.28.0 → magic_pocket_cli-0.28.2}/pocket_cli/templates/cloudformation/awscontainer.yaml +0 -0
  66. {magic_pocket_cli-0.28.0 → magic_pocket_cli-0.28.2}/pocket_cli/templates/cloudformation/cf_function_api_host.js +0 -0
  67. {magic_pocket_cli-0.28.0 → magic_pocket_cli-0.28.2}/pocket_cli/templates/cloudformation/cf_function_basic_auth_prelude.js +0 -0
  68. {magic_pocket_cli-0.28.0 → magic_pocket_cli-0.28.2}/pocket_cli/templates/cloudformation/cf_function_host_redirect_prelude.js +0 -0
  69. {magic_pocket_cli-0.28.0 → magic_pocket_cli-0.28.2}/pocket_cli/templates/cloudformation/cf_function_spa_auth.js +0 -0
  70. {magic_pocket_cli-0.28.0 → magic_pocket_cli-0.28.2}/pocket_cli/templates/cloudformation/cf_function_spa_fallback.js +0 -0
  71. {magic_pocket_cli-0.28.0 → magic_pocket_cli-0.28.2}/pocket_cli/templates/cloudformation/cloudfront.yaml +0 -0
  72. {magic_pocket_cli-0.28.0 → magic_pocket_cli-0.28.2}/pocket_cli/templates/cloudformation/cloudfront_acm.yaml +0 -0
  73. {magic_pocket_cli-0.28.0 → magic_pocket_cli-0.28.2}/pocket_cli/templates/cloudformation/cloudfront_keys.yaml +0 -0
  74. {magic_pocket_cli-0.28.0 → magic_pocket_cli-0.28.2}/pocket_cli/templates/cloudformation/cloudfront_waf.yaml +0 -0
  75. {magic_pocket_cli-0.28.0 → magic_pocket_cli-0.28.2}/pocket_cli/templates/cloudformation/vpc.yaml +0 -0
  76. {magic_pocket_cli-0.28.0 → magic_pocket_cli-0.28.2}/pocket_cli/templates/init/django-dotenv.env +0 -0
  77. {magic_pocket_cli-0.28.0 → magic_pocket_cli-0.28.2}/pocket_cli/templates/init/django-settings.py +0 -0
  78. {magic_pocket_cli-0.28.0 → magic_pocket_cli-0.28.2}/pocket_cli/templates/init/pocket.Dockerfile +0 -0
  79. {magic_pocket_cli-0.28.0 → magic_pocket_cli-0.28.2}/pocket_cli/templates/init/pocket_simple.toml +0 -0
@@ -1,13 +1,13 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: magic-pocket-cli
3
- Version: 0.28.0
3
+ Version: 0.28.2
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.29,>=0.28.0
10
+ Requires-Dist: magic-pocket<0.29,>=0.28.2
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
@@ -33,7 +33,9 @@ def _append_infra_resources(resources, context: Context, state_bucket: str):
33
33
  resources.append(Dsql(context.dsql))
34
34
  if context.rds:
35
35
  resources.append(Rds(context.rds))
36
- if context.backup and context.backup.declared:
36
+ # declared でなくても組み込む (0.27 以前の旧形式 plan の掃除が
37
+ # ensure_post_deploy_state で走るため)
38
+ if context.backup and (context.backup.declared or context.backup.legacy_plan_names):
37
39
  resources.append(
38
40
  Backup(context.backup, dsql_context=context.dsql, rds_context=context.rds)
39
41
  )
@@ -93,6 +93,9 @@ class Backup:
93
93
 
94
94
  @property
95
95
  def description(self):
96
+ if not self.context.plans:
97
+ # legacy 掃除のためだけに組み込まれた場合 ([backup] 未宣言)
98
+ return "Clean up legacy AWS Backup plans"
96
99
  return "Configure AWS Backup plans: %s" % ", ".join(
97
100
  plan.plan_name for plan in self.context.plans
98
101
  )
@@ -132,9 +135,12 @@ class Backup:
132
135
  def ensure_post_deploy_state(self):
133
136
  """宣言されたエンジンごとに plan / selection を冪等に確保する。
134
137
 
135
- 未宣言なら何もしない (opt-in。宣言を外した後の既存 plan にも触らない。
136
- plan の掃除は destroy の責務)。
138
+ 未宣言なら plan を作らない (opt-in。宣言を外した後の既存 plan にも
139
+ 触らない。plan の掃除は destroy の責務)。ただし 0.27 以前の旧形式名の
140
+ plan は例外で、宣言に依らず削除する (改名で孤児化しており、残存すると
141
+ 新 plan と二重にバックアップが走るため)。
137
142
  """
143
+ self._cleanup_legacy_plans()
138
144
  if not self.context.plans:
139
145
  return
140
146
  try:
@@ -158,10 +164,33 @@ class Backup:
158
164
  " provisioning をスキップしました: %s" % e
159
165
  )
160
166
  echo.warning(
161
- " deploy role に backup:* を付与してください"
162
- " (`pocket permissions list` 参照)。"
167
+ " deploy role に backup グループの権限 (backup:* と kms 系)"
168
+ " を付与してください (`pocket permissions list` 参照)。"
163
169
  )
164
170
 
171
+ def _cleanup_legacy_plans(self) -> None:
172
+ """0.27 以前の旧形式名 ({prefix}dsql-backup) の plan を削除する。
173
+
174
+ 0.28.0 で plan 名が {prefix}backup-dsql に変わったため、旧名の plan は
175
+ どのコードからも参照されない孤児になり、放置すると新 plan と同じ
176
+ cluster に二重で走る。権限が無ければ警告に留める (deploy を落とさない)。
177
+ """
178
+ for plan_name in self.context.legacy_plan_names:
179
+ try:
180
+ plan_id = self._find_plan_id(plan_name)
181
+ if plan_id is None:
182
+ continue
183
+ echo.log("Deleting legacy backup plan: %s" % plan_name)
184
+ self._delete_plan(plan_id)
185
+ except ClientError as e:
186
+ if e.response["Error"]["Code"] != "AccessDeniedException":
187
+ raise
188
+ echo.warning(
189
+ "旧形式の backup plan (%s) を確認する権限が無いため、"
190
+ "掃除をスキップしました。plan が残っている場合は二重に"
191
+ "バックアップが走るため、手動で削除してください。" % plan_name
192
+ )
193
+
165
194
  def _plan_document(self, plan: BackupPlanContext) -> dict:
166
195
  rules = []
167
196
  for rule in plan.rules:
@@ -273,14 +302,18 @@ class Backup:
273
302
  return
274
303
  if plan_id is None:
275
304
  continue
276
- selections = self._backup.list_backup_selections(BackupPlanId=plan_id)
277
- for item in selections["BackupSelectionsList"]:
278
- self._backup.delete_backup_selection(
279
- BackupPlanId=plan_id, SelectionId=item["SelectionId"]
280
- )
281
- self._backup.delete_backup_plan(BackupPlanId=plan_id)
305
+ self._delete_plan(plan_id)
282
306
  echo.log("Deleted backup plan: %s" % plan_name)
283
307
 
308
+ def _delete_plan(self, plan_id: str) -> None:
309
+ """selection → plan の順に削除する (selection が残ると plan を消せない)。"""
310
+ selections = self._backup.list_backup_selections(BackupPlanId=plan_id)
311
+ for item in selections["BackupSelectionsList"]:
312
+ self._backup.delete_backup_selection(
313
+ BackupPlanId=plan_id, SelectionId=item["SelectionId"]
314
+ )
315
+ self._backup.delete_backup_plan(BackupPlanId=plan_id)
316
+
284
317
  def list_recovery_points(self) -> list[dict] | None:
285
318
  """対象 DB の recovery point 一覧 (pocket-backup vault 内のもの)。
286
319
 
@@ -10,6 +10,7 @@ from pathlib import Path
10
10
  from typing import TYPE_CHECKING, Literal
11
11
 
12
12
  import boto3
13
+ from boto3.s3.transfer import TransferConfig
13
14
  from botocore.exceptions import ClientError
14
15
  from pydantic import BaseModel
15
16
 
@@ -22,6 +23,14 @@ if TYPE_CHECKING:
22
23
  from pocket.context import CloudFrontContext, RouteContext
23
24
  from pocket_cli.mediator import Mediator
24
25
 
26
+ # upload_dir の差分 skip (_is_unchanged) は ETag が素の MD5 であることに依存する。
27
+ # boto3 既定の multipart 閾値 (8MB) を超えると ETag が `<md5-of-part-md5s>-<N>`
28
+ # 形式になり、そのファイルは内容不変でも毎 deploy「変更あり」= 再アップロード +
29
+ # route 全体の invalidation になってしまう。単一 PUT の上限 (5GB) に対して
30
+ # 十分安全な 128MB まで multipart を使わない。これを超えるファイルは従来どおり
31
+ # 常に再アップロードされる (_is_unchanged の docstring 参照)
32
+ _UPLOAD_TRANSFER_CONFIG = TransferConfig(multipart_threshold=128 * 1024 * 1024)
33
+
25
34
 
26
35
  class OriginAccessControl(BaseModel):
27
36
  Id: str
@@ -334,6 +343,7 @@ class CloudFront:
334
343
  self.context.bucket_name,
335
344
  s3_key,
336
345
  ExtraArgs=extra_args,
346
+ Config=_UPLOAD_TRANSFER_CONFIG,
337
347
  )
338
348
  uploaded += 1
339
349
  echo.log("アップロード: s3://%s/%s" % (self.context.bucket_name, s3_key))
@@ -562,6 +572,13 @@ def _is_unchanged(file: Path, etag: str | None) -> bool:
562
572
  分からないとローカルから再現できない。SSE-KMS 等でも ETag は MD5 に
563
573
  ならない。これらは「不明」= 変更ありとして扱い、常に再アップロードする。
564
574
 
575
+ このため upload 側は multipart 閾値を 128MB に引き上げている
576
+ (_UPLOAD_TRANSFER_CONFIG)。boto3 既定の 8MB のままでは、8MB 超のファイルが
577
+ 1 個あるだけで毎 deploy 再アップロード + route 全体の invalidation が走る。
578
+ 過去に multipart で上がった既存オブジェクトも、閾値以下なら次の deploy で
579
+ 単一 PUT により上げ直され、以後は skip が効くようになる (自己回復)。
580
+ 128MB 超のファイルだけは引き続き毎回再アップロードになる。
581
+
565
582
  サイズ比較での代替はしない。同じサイズで内容が違うファイルを「変更なし」と
566
583
  誤判定すると、そのファイルは以後どの deploy でも更新されなくなる。
567
584
  誤って skip する事故に比べれば、再アップロードのコストは安い。
@@ -1,12 +1,12 @@
1
1
  [project]
2
2
  name = "magic-pocket-cli"
3
- version = "0.28.0"
3
+ version = "0.28.2"
4
4
  description = "CLI and deploy tools for magic-pocket."
5
5
  requires-python = ">=3.10"
6
6
  dependencies = [
7
7
  # lib は同 minor に束縛する。0.x では minor が breaking の単位のため、
8
8
  # 上限なしだと cli を pin した消費者にも lib の破壊的変更が届いてしまう
9
- "magic-pocket>=0.28.0,<0.29",
9
+ "magic-pocket>=0.28.2,<0.29",
10
10
  "click>=8.1.7",
11
11
  "jinja2>=3.1.3",
12
12
  "python-on-whales>=0.68.0",