magic-pocket-cli 0.32.1__tar.gz → 0.34.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 (81) hide show
  1. {magic_pocket_cli-0.32.1 → magic_pocket_cli-0.34.0}/PKG-INFO +2 -2
  2. {magic_pocket_cli-0.32.1 → magic_pocket_cli-0.34.0}/pocket_cli/cli/deploy_cli.py +4 -0
  3. {magic_pocket_cli-0.32.1 → magic_pocket_cli-0.34.0}/pocket_cli/cli/status_cli.py +3 -0
  4. magic_pocket_cli-0.34.0/pocket_cli/resources/sqs_alert.py +94 -0
  5. {magic_pocket_cli-0.32.1 → magic_pocket_cli-0.34.0}/pocket_cli/templates/cloudformation/container.yaml +42 -0
  6. {magic_pocket_cli-0.32.1 → magic_pocket_cli-0.34.0}/pyproject.toml +2 -2
  7. {magic_pocket_cli-0.32.1 → magic_pocket_cli-0.34.0}/.gitignore +0 -0
  8. {magic_pocket_cli-0.32.1 → magic_pocket_cli-0.34.0}/pocket_cli/__init__.py +0 -0
  9. {magic_pocket_cli-0.32.1 → magic_pocket_cli-0.34.0}/pocket_cli/cli/__init__.py +0 -0
  10. {magic_pocket_cli-0.32.1 → magic_pocket_cli-0.34.0}/pocket_cli/cli/aws_auth.py +0 -0
  11. {magic_pocket_cli-0.32.1 → magic_pocket_cli-0.34.0}/pocket_cli/cli/backup_cli.py +0 -0
  12. {magic_pocket_cli-0.32.1 → magic_pocket_cli-0.34.0}/pocket_cli/cli/cloudfront_cli.py +0 -0
  13. {magic_pocket_cli-0.32.1 → magic_pocket_cli-0.34.0}/pocket_cli/cli/cloudfront_keys_cli.py +0 -0
  14. {magic_pocket_cli-0.32.1 → magic_pocket_cli-0.34.0}/pocket_cli/cli/cloudfront_waf_cli.py +0 -0
  15. {magic_pocket_cli-0.32.1 → magic_pocket_cli-0.34.0}/pocket_cli/cli/container_cli.py +0 -0
  16. {magic_pocket_cli-0.32.1 → magic_pocket_cli-0.34.0}/pocket_cli/cli/destroy_cli.py +0 -0
  17. {magic_pocket_cli-0.32.1 → magic_pocket_cli-0.34.0}/pocket_cli/cli/dsql_cli.py +0 -0
  18. {magic_pocket_cli-0.32.1 → magic_pocket_cli-0.34.0}/pocket_cli/cli/image_cli.py +0 -0
  19. {magic_pocket_cli-0.32.1 → magic_pocket_cli-0.34.0}/pocket_cli/cli/interaction.py +0 -0
  20. {magic_pocket_cli-0.32.1 → magic_pocket_cli-0.34.0}/pocket_cli/cli/main_cli.py +0 -0
  21. {magic_pocket_cli-0.32.1 → magic_pocket_cli-0.34.0}/pocket_cli/cli/migrate_cli.py +0 -0
  22. {magic_pocket_cli-0.32.1 → magic_pocket_cli-0.34.0}/pocket_cli/cli/neon_cli.py +0 -0
  23. {magic_pocket_cli-0.32.1 → magic_pocket_cli-0.34.0}/pocket_cli/cli/permissions_cli.py +0 -0
  24. {magic_pocket_cli-0.32.1 → magic_pocket_cli-0.34.0}/pocket_cli/cli/rds_cli.py +0 -0
  25. {magic_pocket_cli-0.32.1 → magic_pocket_cli-0.34.0}/pocket_cli/cli/removed_flags.py +0 -0
  26. {magic_pocket_cli-0.32.1 → magic_pocket_cli-0.34.0}/pocket_cli/cli/resource_helper.py +0 -0
  27. {magic_pocket_cli-0.32.1 → magic_pocket_cli-0.34.0}/pocket_cli/cli/runtime_config_cli.py +0 -0
  28. {magic_pocket_cli-0.32.1 → magic_pocket_cli-0.34.0}/pocket_cli/cli/s3_cli.py +0 -0
  29. {magic_pocket_cli-0.32.1 → magic_pocket_cli-0.34.0}/pocket_cli/cli/store_url_helper.py +0 -0
  30. {magic_pocket_cli-0.32.1 → magic_pocket_cli-0.34.0}/pocket_cli/cli/tidb_cli.py +0 -0
  31. {magic_pocket_cli-0.32.1 → magic_pocket_cli-0.34.0}/pocket_cli/cli/upstash_cli.py +0 -0
  32. {magic_pocket_cli-0.32.1 → magic_pocket_cli-0.34.0}/pocket_cli/cli/url_helper.py +0 -0
  33. {magic_pocket_cli-0.32.1 → magic_pocket_cli-0.34.0}/pocket_cli/cli/vpc_cli.py +0 -0
  34. {magic_pocket_cli-0.32.1 → magic_pocket_cli-0.34.0}/pocket_cli/cli/waf_cli.py +0 -0
  35. {magic_pocket_cli-0.32.1 → magic_pocket_cli-0.34.0}/pocket_cli/django_cli.py +0 -0
  36. {magic_pocket_cli-0.32.1 → magic_pocket_cli-0.34.0}/pocket_cli/mediator.py +0 -0
  37. {magic_pocket_cli-0.32.1 → magic_pocket_cli-0.34.0}/pocket_cli/migrations.py +0 -0
  38. {magic_pocket_cli-0.32.1 → magic_pocket_cli-0.34.0}/pocket_cli/resources/__init__.py +0 -0
  39. {magic_pocket_cli-0.32.1 → magic_pocket_cli-0.34.0}/pocket_cli/resources/aws/__init__.py +0 -0
  40. {magic_pocket_cli-0.32.1 → magic_pocket_cli-0.34.0}/pocket_cli/resources/aws/backup_common.py +0 -0
  41. {magic_pocket_cli-0.32.1 → magic_pocket_cli-0.34.0}/pocket_cli/resources/aws/builders/__init__.py +0 -0
  42. {magic_pocket_cli-0.32.1 → magic_pocket_cli-0.34.0}/pocket_cli/resources/aws/builders/codebuild.py +0 -0
  43. {magic_pocket_cli-0.32.1 → magic_pocket_cli-0.34.0}/pocket_cli/resources/aws/builders/context_check.py +0 -0
  44. {magic_pocket_cli-0.32.1 → magic_pocket_cli-0.34.0}/pocket_cli/resources/aws/builders/depot.py +0 -0
  45. {magic_pocket_cli-0.32.1 → magic_pocket_cli-0.34.0}/pocket_cli/resources/aws/builders/docker.py +0 -0
  46. {magic_pocket_cli-0.32.1 → magic_pocket_cli-0.34.0}/pocket_cli/resources/aws/builders/dockerignore.py +0 -0
  47. {magic_pocket_cli-0.32.1 → magic_pocket_cli-0.34.0}/pocket_cli/resources/aws/cloudformation.py +0 -0
  48. {magic_pocket_cli-0.32.1 → magic_pocket_cli-0.34.0}/pocket_cli/resources/aws/ecr.py +0 -0
  49. {magic_pocket_cli-0.32.1 → magic_pocket_cli-0.34.0}/pocket_cli/resources/aws/efs.py +0 -0
  50. {magic_pocket_cli-0.32.1 → magic_pocket_cli-0.34.0}/pocket_cli/resources/aws/lambdahandler.py +0 -0
  51. {magic_pocket_cli-0.32.1 → magic_pocket_cli-0.34.0}/pocket_cli/resources/aws/poll.py +0 -0
  52. {magic_pocket_cli-0.32.1 → magic_pocket_cli-0.34.0}/pocket_cli/resources/aws/s3_utils.py +0 -0
  53. {magic_pocket_cli-0.32.1 → magic_pocket_cli-0.34.0}/pocket_cli/resources/aws/stack_backed.py +0 -0
  54. {magic_pocket_cli-0.32.1 → magic_pocket_cli-0.34.0}/pocket_cli/resources/aws/state.py +0 -0
  55. {magic_pocket_cli-0.32.1 → magic_pocket_cli-0.34.0}/pocket_cli/resources/backup.py +0 -0
  56. {magic_pocket_cli-0.32.1 → magic_pocket_cli-0.34.0}/pocket_cli/resources/cloudfront.py +0 -0
  57. {magic_pocket_cli-0.32.1 → magic_pocket_cli-0.34.0}/pocket_cli/resources/cloudfront_acm.py +0 -0
  58. {magic_pocket_cli-0.32.1 → magic_pocket_cli-0.34.0}/pocket_cli/resources/cloudfront_keys.py +0 -0
  59. {magic_pocket_cli-0.32.1 → magic_pocket_cli-0.34.0}/pocket_cli/resources/cloudfront_waf.py +0 -0
  60. {magic_pocket_cli-0.32.1 → magic_pocket_cli-0.34.0}/pocket_cli/resources/container.py +0 -0
  61. {magic_pocket_cli-0.32.1 → magic_pocket_cli-0.34.0}/pocket_cli/resources/dsql.py +0 -0
  62. {magic_pocket_cli-0.32.1 → magic_pocket_cli-0.34.0}/pocket_cli/resources/neon.py +0 -0
  63. {magic_pocket_cli-0.32.1 → magic_pocket_cli-0.34.0}/pocket_cli/resources/rds.py +0 -0
  64. {magic_pocket_cli-0.32.1 → magic_pocket_cli-0.34.0}/pocket_cli/resources/s3.py +0 -0
  65. {magic_pocket_cli-0.32.1 → magic_pocket_cli-0.34.0}/pocket_cli/resources/tidb.py +0 -0
  66. {magic_pocket_cli-0.32.1 → magic_pocket_cli-0.34.0}/pocket_cli/resources/upstash.py +0 -0
  67. {magic_pocket_cli-0.32.1 → magic_pocket_cli-0.34.0}/pocket_cli/resources/vpc.py +0 -0
  68. {magic_pocket_cli-0.32.1 → magic_pocket_cli-0.34.0}/pocket_cli/templates/cloudformation/cf_function_api_host.js +0 -0
  69. {magic_pocket_cli-0.32.1 → magic_pocket_cli-0.34.0}/pocket_cli/templates/cloudformation/cf_function_basic_auth_prelude.js +0 -0
  70. {magic_pocket_cli-0.32.1 → magic_pocket_cli-0.34.0}/pocket_cli/templates/cloudformation/cf_function_host_redirect_prelude.js +0 -0
  71. {magic_pocket_cli-0.32.1 → magic_pocket_cli-0.34.0}/pocket_cli/templates/cloudformation/cf_function_spa_auth.js +0 -0
  72. {magic_pocket_cli-0.32.1 → magic_pocket_cli-0.34.0}/pocket_cli/templates/cloudformation/cf_function_spa_fallback.js +0 -0
  73. {magic_pocket_cli-0.32.1 → magic_pocket_cli-0.34.0}/pocket_cli/templates/cloudformation/cloudfront.yaml +0 -0
  74. {magic_pocket_cli-0.32.1 → magic_pocket_cli-0.34.0}/pocket_cli/templates/cloudformation/cloudfront_acm.yaml +0 -0
  75. {magic_pocket_cli-0.32.1 → magic_pocket_cli-0.34.0}/pocket_cli/templates/cloudformation/cloudfront_keys.yaml +0 -0
  76. {magic_pocket_cli-0.32.1 → magic_pocket_cli-0.34.0}/pocket_cli/templates/cloudformation/cloudfront_waf.yaml +0 -0
  77. {magic_pocket_cli-0.32.1 → magic_pocket_cli-0.34.0}/pocket_cli/templates/cloudformation/vpc.yaml +0 -0
  78. {magic_pocket_cli-0.32.1 → magic_pocket_cli-0.34.0}/pocket_cli/templates/init/django-dotenv.env +0 -0
  79. {magic_pocket_cli-0.32.1 → magic_pocket_cli-0.34.0}/pocket_cli/templates/init/django-settings.py +0 -0
  80. {magic_pocket_cli-0.32.1 → magic_pocket_cli-0.34.0}/pocket_cli/templates/init/pocket.Dockerfile +0 -0
  81. {magic_pocket_cli-0.32.1 → magic_pocket_cli-0.34.0}/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.32.1
3
+ Version: 0.34.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.33,>=0.32.1
10
+ Requires-Dist: magic-pocket<0.35,>=0.34.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
@@ -23,6 +23,7 @@ from pocket_cli.resources.dsql import Dsql
23
23
  from pocket_cli.resources.neon import Neon
24
24
  from pocket_cli.resources.rds import Rds
25
25
  from pocket_cli.resources.s3 import S3
26
+ from pocket_cli.resources.sqs_alert import echo_dead_letter_alert_warnings
26
27
  from pocket_cli.resources.tidb import TiDb
27
28
  from pocket_cli.resources.upstash import Upstash
28
29
  from pocket_cli.resources.vpc import Vpc
@@ -225,6 +226,9 @@ def _deploy_pipeline(context: Context, *, openpath=None, skip_frontend=False):
225
226
  deploy_frontend(context)
226
227
  # build 時の other-read 警告はログに埋もれて気付けないため最後に再掲する
227
228
  resummarize_world_read_warnings()
229
+ # DLQ アラートの email 購読が未確認のままだと通知が届かないため、
230
+ # deploy の最後に購読状態を確認して警告する
231
+ echo_dead_letter_alert_warnings(context)
228
232
  # デプロイ完了後の URL 表示
229
233
  url = _get_deploy_url(context)
230
234
  if url:
@@ -5,6 +5,7 @@ from pocket.resources.aws.secretsmanager import PocketSecretIsNotReady
5
5
  from pocket.utils import echo
6
6
  from pocket_cli.cli.deploy_cli import get_resources
7
7
  from pocket_cli.resources.container import Container
8
+ from pocket_cli.resources.sqs_alert import echo_dead_letter_alert_statuses
8
9
 
9
10
 
10
11
  def show_status_message(resource):
@@ -56,3 +57,5 @@ def status(stage, show_info):
56
57
  show_status_message(resource)
57
58
  if show_info:
58
59
  show_info_message(resource)
60
+ # DLQ アラートの email 購読状態 (PendingConfirmation のままだと通知が届かない)
61
+ echo_dead_letter_alert_statuses(context)
@@ -0,0 +1,94 @@
1
+ """SQS DLQ アラートの SNS email 購読状態の照会。
2
+
3
+ SNS の email 購読は、宛先が確認メールのリンクを踏むまで ``PendingConfirmation``
4
+ のままで、その間はアラームが鳴っても通知が届かない (「監視してあるつもり」の
5
+ 無監視)。deploy の最後と ``pocket status`` で購読状態を可視化し、未確認のまま
6
+ 放置されるのを防ぐ (KN1110)。
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ from typing import Literal, NamedTuple
12
+
13
+ import boto3
14
+
15
+ from pocket.context import Context
16
+ from pocket.utils import echo
17
+
18
+ SubscriptionState = Literal["Confirmed", "PendingConfirmation", "NotCreated"]
19
+
20
+
21
+ class DeadLetterAlertStatus(NamedTuple):
22
+ topic_name: str
23
+ email: str
24
+ state: SubscriptionState
25
+
26
+
27
+ def dead_letter_alert_statuses(context: Context) -> list[DeadLetterAlertStatus]:
28
+ """dead_letter_alert が enabled な handler ごとの email 購読状態を返す。
29
+
30
+ topic 未作成 (deploy 前 / 削除済み) は ``NotCreated``。購読が確認済みなら
31
+ ``Confirmed``、確認メール未対応なら ``PendingConfirmation``。
32
+ """
33
+ targets = [
34
+ (c.region, h.sqs.dead_letter_alert.topic_name, h.sqs.dead_letter_alert.email)
35
+ for c in context.container.values()
36
+ for h in c.handlers.values()
37
+ if h.sqs and h.sqs.dead_letter_alert
38
+ ]
39
+ if not targets:
40
+ return []
41
+ account_id = boto3.client("sts").get_caller_identity()["Account"]
42
+ statuses: list[DeadLetterAlertStatus] = []
43
+ for region, topic_name, email in targets:
44
+ sns = boto3.client("sns", region_name=region)
45
+ topic_arn = f"arn:aws:sns:{region}:{account_id}:{topic_name}"
46
+ try:
47
+ res = sns.list_subscriptions_by_topic(TopicArn=topic_arn)
48
+ except sns.exceptions.NotFoundException:
49
+ statuses.append(DeadLetterAlertStatus(topic_name, email, "NotCreated"))
50
+ continue
51
+ state: SubscriptionState = "NotCreated"
52
+ for sub in res["Subscriptions"]:
53
+ if sub["Protocol"] != "email" or sub["Endpoint"] != email:
54
+ continue
55
+ if sub["SubscriptionArn"] == "PendingConfirmation":
56
+ state = "PendingConfirmation"
57
+ else:
58
+ state = "Confirmed"
59
+ break
60
+ statuses.append(DeadLetterAlertStatus(topic_name, email, state))
61
+ return statuses
62
+
63
+
64
+ def echo_dead_letter_alert_warnings(context: Context) -> None:
65
+ """未確認の購読を警告する (deploy の最後に呼ぶ)。
66
+
67
+ 初回 deploy は宛先が確認メールを踏むまで必ず ``PendingConfirmation`` に
68
+ なるため、エラーではなく警告に留める (エラーにすると初回 deploy が
69
+ 必ず落ちる)。確認済みなら何も出さない。
70
+ """
71
+ for status in dead_letter_alert_statuses(context):
72
+ if status.state == "Confirmed":
73
+ continue
74
+ if status.state == "PendingConfirmation":
75
+ echo.warning(
76
+ f"DLQ alert subscription for {status.email} ({status.topic_name}) "
77
+ "is still PendingConfirmation. Alarm notifications will NOT be "
78
+ "delivered until the confirmation link in the email is clicked."
79
+ )
80
+ else:
81
+ echo.warning(
82
+ f"DLQ alert subscription for {status.email} ({status.topic_name}) "
83
+ "was not found. Check the container stack deployment."
84
+ )
85
+
86
+
87
+ def echo_dead_letter_alert_statuses(context: Context) -> None:
88
+ """購読状態を一覧表示する (``pocket status`` 用)。"""
89
+ for status in dead_letter_alert_statuses(context):
90
+ message = f"DLQ alert {status.topic_name} -> {status.email}: {status.state}"
91
+ if status.state == "Confirmed":
92
+ echo.success(message)
93
+ else:
94
+ echo.warning(message)
@@ -400,6 +400,48 @@ Resources:
400
400
  # max 14 days
401
401
  MessageRetentionPeriod: "{{ handler.sqs.dead_letter_message_retention_period }}"
402
402
 
403
+ # {% if handler.sqs.dead_letter_alert %}
404
+ # DLQ 到達の通知 (dead_letter_alert 宣言時のみ)。email 購読は宛先が確認メールの
405
+ # リンクを踏むまで PendingConfirmation のままで通知が届かない点に注意
406
+ # (deploy 後の購読状態チェックと pocket status で可視化する)
407
+ "{{ handler.key|capitalize }}DeadLetterAlertTopic":
408
+ Type: AWS::SNS::Topic
409
+ Properties:
410
+ TopicName: "{{ handler.sqs.dead_letter_alert.topic_name }}"
411
+
412
+ "{{ handler.key|capitalize }}DeadLetterAlertSubscription":
413
+ Type: AWS::SNS::Subscription
414
+ Properties:
415
+ TopicArn:
416
+ Ref: "{{ handler.key|capitalize }}DeadLetterAlertTopic"
417
+ Protocol: email
418
+ Endpoint: "{{ handler.sqs.dead_letter_alert.email }}"
419
+
420
+ "{{ handler.key|capitalize }}DeadLetterAlarm":
421
+ Type: AWS::CloudWatch::Alarm
422
+ Properties:
423
+ AlarmName: "{{ handler.sqs.dead_letter_alert.alarm_name }}"
424
+ AlarmDescription: "Messages arrived in the dead-letter queue of {{ handler.sqs.name }}"
425
+ Namespace: "AWS/SQS"
426
+ MetricName: ApproximateNumberOfMessagesVisible
427
+ Dimensions:
428
+ - Name: QueueName
429
+ Value:
430
+ Fn::GetAtt: "{{ handler.key|capitalize }}DeadLetterQueue.QueueName"
431
+ Statistic: Maximum
432
+ Period: 300
433
+ EvaluationPeriods: 1
434
+ Threshold: 1
435
+ ComparisonOperator: GreaterThanOrEqualToThreshold
436
+ # DLQ が空の間はメトリクス欠測になりうるため、欠測 = 正常として扱う
437
+ TreatMissingData: notBreaching
438
+ AlarmActions:
439
+ - Ref: "{{ handler.key|capitalize }}DeadLetterAlertTopic"
440
+ # 復旧 (redrive / purge で DLQ が空に戻った) も通知する
441
+ OKActions:
442
+ - Ref: "{{ handler.key|capitalize }}DeadLetterAlertTopic"
443
+ # {% endif %}
444
+
403
445
  "{{ handler.key|capitalize }}SqsEventSourceMapping":
404
446
  DependsOn:
405
447
  - "{{ handler.key|capitalize }}SqsQueue"
@@ -1,12 +1,12 @@
1
1
  [project]
2
2
  name = "magic-pocket-cli"
3
- version = "0.32.1"
3
+ version = "0.34.0"
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.32.1,<0.33",
9
+ "magic-pocket>=0.34.0,<0.35",
10
10
  "click>=8.1.7",
11
11
  "jinja2>=3.1.3",
12
12
  "python-on-whales>=0.68.0",