magic-pocket-cli 0.40.0__tar.gz → 0.41.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.
- {magic_pocket_cli-0.40.0 → magic_pocket_cli-0.41.0}/PKG-INFO +2 -2
- {magic_pocket_cli-0.40.0 → magic_pocket_cli-0.41.0}/pocket_cli/cli/deploy_cli.py +3 -0
- {magic_pocket_cli-0.40.0 → magic_pocket_cli-0.41.0}/pocket_cli/cli/destroy_cli.py +1 -0
- magic_pocket_cli-0.41.0/pocket_cli/cli/permissions_cli.py +96 -0
- {magic_pocket_cli-0.40.0 → magic_pocket_cli-0.41.0}/pocket_cli/resources/aws/builders/__init__.py +2 -0
- {magic_pocket_cli-0.40.0 → magic_pocket_cli-0.41.0}/pocket_cli/resources/aws/builders/codebuild.py +20 -13
- {magic_pocket_cli-0.40.0 → magic_pocket_cli-0.41.0}/pocket_cli/resources/aws/cloudformation.py +88 -39
- {magic_pocket_cli-0.40.0 → magic_pocket_cli-0.41.0}/pocket_cli/resources/aws/iam_roles.py +293 -49
- magic_pocket_cli-0.41.0/pocket_cli/resources/aws/stage_roles.py +88 -0
- {magic_pocket_cli-0.40.0 → magic_pocket_cli-0.41.0}/pocket_cli/resources/backup.py +5 -1
- {magic_pocket_cli-0.40.0 → magic_pocket_cli-0.41.0}/pocket_cli/resources/container.py +1 -0
- {magic_pocket_cli-0.40.0 → magic_pocket_cli-0.41.0}/pocket_cli/resources/dsql.py +1 -0
- {magic_pocket_cli-0.40.0 → magic_pocket_cli-0.41.0}/pocket_cli/resources/rds.py +0 -10
- magic_pocket_cli-0.41.0/pocket_cli/templates/cloudformation/cf_function_cache_control.js +8 -0
- magic_pocket_cli-0.41.0/pocket_cli/templates/cloudformation/cf_function_deploy_hash_strip.js +19 -0
- {magic_pocket_cli-0.40.0 → magic_pocket_cli-0.41.0}/pocket_cli/templates/cloudformation/cloudfront.yaml +60 -28
- {magic_pocket_cli-0.40.0 → magic_pocket_cli-0.41.0}/pocket_cli/templates/cloudformation/container.yaml +10 -4
- {magic_pocket_cli-0.40.0 → magic_pocket_cli-0.41.0}/pyproject.toml +2 -2
- magic_pocket_cli-0.40.0/pocket_cli/cli/permissions_cli.py +0 -46
- {magic_pocket_cli-0.40.0 → magic_pocket_cli-0.41.0}/.gitignore +0 -0
- {magic_pocket_cli-0.40.0 → magic_pocket_cli-0.41.0}/pocket_cli/__init__.py +0 -0
- {magic_pocket_cli-0.40.0 → magic_pocket_cli-0.41.0}/pocket_cli/cli/__init__.py +0 -0
- {magic_pocket_cli-0.40.0 → magic_pocket_cli-0.41.0}/pocket_cli/cli/aws_auth.py +0 -0
- {magic_pocket_cli-0.40.0 → magic_pocket_cli-0.41.0}/pocket_cli/cli/backup_cli.py +0 -0
- {magic_pocket_cli-0.40.0 → magic_pocket_cli-0.41.0}/pocket_cli/cli/build_cli.py +0 -0
- {magic_pocket_cli-0.40.0 → magic_pocket_cli-0.41.0}/pocket_cli/cli/cleanup_deprecated_cli.py +0 -0
- {magic_pocket_cli-0.40.0 → magic_pocket_cli-0.41.0}/pocket_cli/cli/cloudfront_cli.py +0 -0
- {magic_pocket_cli-0.40.0 → magic_pocket_cli-0.41.0}/pocket_cli/cli/cloudfront_keys_cli.py +0 -0
- {magic_pocket_cli-0.40.0 → magic_pocket_cli-0.41.0}/pocket_cli/cli/cloudfront_waf_cli.py +0 -0
- {magic_pocket_cli-0.40.0 → magic_pocket_cli-0.41.0}/pocket_cli/cli/container_cli.py +0 -0
- {magic_pocket_cli-0.40.0 → magic_pocket_cli-0.41.0}/pocket_cli/cli/dsql_cli.py +0 -0
- {magic_pocket_cli-0.40.0 → magic_pocket_cli-0.41.0}/pocket_cli/cli/image_cli.py +0 -0
- {magic_pocket_cli-0.40.0 → magic_pocket_cli-0.41.0}/pocket_cli/cli/inbound_cli.py +0 -0
- {magic_pocket_cli-0.40.0 → magic_pocket_cli-0.41.0}/pocket_cli/cli/interaction.py +0 -0
- {magic_pocket_cli-0.40.0 → magic_pocket_cli-0.41.0}/pocket_cli/cli/main_cli.py +0 -0
- {magic_pocket_cli-0.40.0 → magic_pocket_cli-0.41.0}/pocket_cli/cli/migrate_cli.py +0 -0
- {magic_pocket_cli-0.40.0 → magic_pocket_cli-0.41.0}/pocket_cli/cli/neon_cli.py +0 -0
- {magic_pocket_cli-0.40.0 → magic_pocket_cli-0.41.0}/pocket_cli/cli/rds_cli.py +0 -0
- {magic_pocket_cli-0.40.0 → magic_pocket_cli-0.41.0}/pocket_cli/cli/removed_flags.py +0 -0
- {magic_pocket_cli-0.40.0 → magic_pocket_cli-0.41.0}/pocket_cli/cli/resource_helper.py +0 -0
- {magic_pocket_cli-0.40.0 → magic_pocket_cli-0.41.0}/pocket_cli/cli/runtime_config_cli.py +0 -0
- {magic_pocket_cli-0.40.0 → magic_pocket_cli-0.41.0}/pocket_cli/cli/s3_cli.py +0 -0
- {magic_pocket_cli-0.40.0 → magic_pocket_cli-0.41.0}/pocket_cli/cli/status_cli.py +0 -0
- {magic_pocket_cli-0.40.0 → magic_pocket_cli-0.41.0}/pocket_cli/cli/store_url_helper.py +0 -0
- {magic_pocket_cli-0.40.0 → magic_pocket_cli-0.41.0}/pocket_cli/cli/tidb_cli.py +0 -0
- {magic_pocket_cli-0.40.0 → magic_pocket_cli-0.41.0}/pocket_cli/cli/upstash_cli.py +0 -0
- {magic_pocket_cli-0.40.0 → magic_pocket_cli-0.41.0}/pocket_cli/cli/url_helper.py +0 -0
- {magic_pocket_cli-0.40.0 → magic_pocket_cli-0.41.0}/pocket_cli/cli/vpc_cli.py +0 -0
- {magic_pocket_cli-0.40.0 → magic_pocket_cli-0.41.0}/pocket_cli/cli/waf_cli.py +0 -0
- {magic_pocket_cli-0.40.0 → magic_pocket_cli-0.41.0}/pocket_cli/django_cli.py +0 -0
- {magic_pocket_cli-0.40.0 → magic_pocket_cli-0.41.0}/pocket_cli/mediator.py +0 -0
- {magic_pocket_cli-0.40.0 → magic_pocket_cli-0.41.0}/pocket_cli/migrations.py +0 -0
- {magic_pocket_cli-0.40.0 → magic_pocket_cli-0.41.0}/pocket_cli/resources/__init__.py +0 -0
- {magic_pocket_cli-0.40.0 → magic_pocket_cli-0.41.0}/pocket_cli/resources/aws/__init__.py +0 -0
- {magic_pocket_cli-0.40.0 → magic_pocket_cli-0.41.0}/pocket_cli/resources/aws/backup_common.py +0 -0
- {magic_pocket_cli-0.40.0 → magic_pocket_cli-0.41.0}/pocket_cli/resources/aws/backup_legacy.py +0 -0
- {magic_pocket_cli-0.40.0 → magic_pocket_cli-0.41.0}/pocket_cli/resources/aws/builders/context_check.py +0 -0
- {magic_pocket_cli-0.40.0 → magic_pocket_cli-0.41.0}/pocket_cli/resources/aws/builders/depot.py +0 -0
- {magic_pocket_cli-0.40.0 → magic_pocket_cli-0.41.0}/pocket_cli/resources/aws/builders/docker.py +0 -0
- {magic_pocket_cli-0.40.0 → magic_pocket_cli-0.41.0}/pocket_cli/resources/aws/builders/dockerignore.py +0 -0
- {magic_pocket_cli-0.40.0 → magic_pocket_cli-0.41.0}/pocket_cli/resources/aws/ecr.py +0 -0
- {magic_pocket_cli-0.40.0 → magic_pocket_cli-0.41.0}/pocket_cli/resources/aws/efs.py +0 -0
- {magic_pocket_cli-0.40.0 → magic_pocket_cli-0.41.0}/pocket_cli/resources/aws/lambdahandler.py +0 -0
- {magic_pocket_cli-0.40.0 → magic_pocket_cli-0.41.0}/pocket_cli/resources/aws/poll.py +0 -0
- {magic_pocket_cli-0.40.0 → magic_pocket_cli-0.41.0}/pocket_cli/resources/aws/s3_utils.py +0 -0
- {magic_pocket_cli-0.40.0 → magic_pocket_cli-0.41.0}/pocket_cli/resources/aws/stack_backed.py +0 -0
- {magic_pocket_cli-0.40.0 → magic_pocket_cli-0.41.0}/pocket_cli/resources/aws/state.py +0 -0
- {magic_pocket_cli-0.40.0 → magic_pocket_cli-0.41.0}/pocket_cli/resources/cloudfront.py +0 -0
- {magic_pocket_cli-0.40.0 → magic_pocket_cli-0.41.0}/pocket_cli/resources/cloudfront_acm.py +0 -0
- {magic_pocket_cli-0.40.0 → magic_pocket_cli-0.41.0}/pocket_cli/resources/cloudfront_keys.py +0 -0
- {magic_pocket_cli-0.40.0 → magic_pocket_cli-0.41.0}/pocket_cli/resources/cloudfront_waf.py +0 -0
- {magic_pocket_cli-0.40.0 → magic_pocket_cli-0.41.0}/pocket_cli/resources/inbound.py +0 -0
- {magic_pocket_cli-0.40.0 → magic_pocket_cli-0.41.0}/pocket_cli/resources/inbound_domain.py +0 -0
- {magic_pocket_cli-0.40.0 → magic_pocket_cli-0.41.0}/pocket_cli/resources/inbound_template.py +0 -0
- {magic_pocket_cli-0.40.0 → magic_pocket_cli-0.41.0}/pocket_cli/resources/neon.py +0 -0
- {magic_pocket_cli-0.40.0 → magic_pocket_cli-0.41.0}/pocket_cli/resources/s3.py +0 -0
- {magic_pocket_cli-0.40.0 → magic_pocket_cli-0.41.0}/pocket_cli/resources/sqs_alert.py +0 -0
- {magic_pocket_cli-0.40.0 → magic_pocket_cli-0.41.0}/pocket_cli/resources/tidb.py +0 -0
- {magic_pocket_cli-0.40.0 → magic_pocket_cli-0.41.0}/pocket_cli/resources/upstash.py +0 -0
- {magic_pocket_cli-0.40.0 → magic_pocket_cli-0.41.0}/pocket_cli/resources/vpc.py +0 -0
- {magic_pocket_cli-0.40.0 → magic_pocket_cli-0.41.0}/pocket_cli/templates/cloudformation/cf_function_api_host.js +0 -0
- {magic_pocket_cli-0.40.0 → magic_pocket_cli-0.41.0}/pocket_cli/templates/cloudformation/cf_function_basic_auth_prelude.js +0 -0
- {magic_pocket_cli-0.40.0 → magic_pocket_cli-0.41.0}/pocket_cli/templates/cloudformation/cf_function_host_redirect_prelude.js +0 -0
- {magic_pocket_cli-0.40.0 → magic_pocket_cli-0.41.0}/pocket_cli/templates/cloudformation/cf_function_spa_auth.js +0 -0
- {magic_pocket_cli-0.40.0 → magic_pocket_cli-0.41.0}/pocket_cli/templates/cloudformation/cf_function_spa_fallback.js +0 -0
- {magic_pocket_cli-0.40.0 → magic_pocket_cli-0.41.0}/pocket_cli/templates/cloudformation/cloudfront_acm.yaml +0 -0
- {magic_pocket_cli-0.40.0 → magic_pocket_cli-0.41.0}/pocket_cli/templates/cloudformation/cloudfront_keys.yaml +0 -0
- {magic_pocket_cli-0.40.0 → magic_pocket_cli-0.41.0}/pocket_cli/templates/cloudformation/cloudfront_waf.yaml +0 -0
- {magic_pocket_cli-0.40.0 → magic_pocket_cli-0.41.0}/pocket_cli/templates/cloudformation/vpc.yaml +0 -0
- {magic_pocket_cli-0.40.0 → magic_pocket_cli-0.41.0}/pocket_cli/templates/init/django-dotenv.env +0 -0
- {magic_pocket_cli-0.40.0 → magic_pocket_cli-0.41.0}/pocket_cli/templates/init/django-settings.py +0 -0
- {magic_pocket_cli-0.40.0 → magic_pocket_cli-0.41.0}/pocket_cli/templates/init/pocket.Dockerfile +0 -0
- {magic_pocket_cli-0.40.0 → magic_pocket_cli-0.41.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.
|
|
3
|
+
Version: 0.41.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.
|
|
10
|
+
Requires-Dist: magic-pocket<0.42,>=0.41.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
|
|
@@ -12,6 +12,7 @@ from pocket_cli.mediator import Mediator
|
|
|
12
12
|
from pocket_cli.resources.aws.builders.context_check import (
|
|
13
13
|
resummarize_world_read_warnings,
|
|
14
14
|
)
|
|
15
|
+
from pocket_cli.resources.aws.stage_roles import verify_stage_roles
|
|
15
16
|
from pocket_cli.resources.aws.state import StateStore, create_state_store
|
|
16
17
|
from pocket_cli.resources.backup import Backup
|
|
17
18
|
from pocket_cli.resources.cloudfront import CloudFront
|
|
@@ -230,6 +231,8 @@ def _deploy_pipeline(context: Context, *, openpath=None, skip_frontend=False):
|
|
|
230
231
|
deploy_hash_message = deploy_hash_report(context)
|
|
231
232
|
if deploy_hash_message:
|
|
232
233
|
echo.info(deploy_hash_message)
|
|
234
|
+
# [iam] external_roles: 事前に作られた role を、何かを変更する前に検査する
|
|
235
|
+
verify_stage_roles(context)
|
|
233
236
|
# CodeBuildがソースアップロードにstate bucketを必要とするため、先に作成
|
|
234
237
|
state_store = _create_state_store(context)
|
|
235
238
|
state_store.ensure_bucket()
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
"""`pocket permissions` サブコマンド群。
|
|
2
|
+
|
|
3
|
+
- `list`: `pocket.toml` の構成から deploy に必要な AWS IAM Action を出力する。
|
|
4
|
+
外部の IAM Role プロビジョニング処理が GitHub Actions デプロイ用 IAM Role を
|
|
5
|
+
作る際の inline policy 生成に使用することを想定。
|
|
6
|
+
- `roles`: pocket の Lambda / scheduler / CodeBuild / AWS Backup が使う IAM role
|
|
7
|
+
の定義を出力する。`[iam] external_roles = true` で role を事前に作る用途。
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
import json
|
|
13
|
+
|
|
14
|
+
import boto3
|
|
15
|
+
import click
|
|
16
|
+
|
|
17
|
+
from pocket.context import Context
|
|
18
|
+
from pocket.permissions import compute_actions
|
|
19
|
+
from pocket.settings import Settings
|
|
20
|
+
from pocket_cli.resources.aws.stage_roles import stage_role_specs
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
@click.group()
|
|
24
|
+
def permissions():
|
|
25
|
+
"""IAM 権限関連のサブコマンド。"""
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
@permissions.command("list")
|
|
29
|
+
@click.option("--stage", envvar="POCKET_DEPLOY_STAGE", prompt=True)
|
|
30
|
+
@click.option(
|
|
31
|
+
"--format",
|
|
32
|
+
"format_",
|
|
33
|
+
type=click.Choice(["text", "json"]),
|
|
34
|
+
default="text",
|
|
35
|
+
show_default=True,
|
|
36
|
+
help='出力形式。text: 1 行 1 Action / json: {"actions": [...]}',
|
|
37
|
+
)
|
|
38
|
+
def list_(stage: str, format_: str):
|
|
39
|
+
"""pocket.toml から必要な AWS Action 一覧を出力する。
|
|
40
|
+
|
|
41
|
+
docs/permissions/aws.md のテーブルに基づき、`[cloudfront]` / `[rds]` /
|
|
42
|
+
`[ses]` などの設定有無に応じて必要 Action を組み立てる。粒度は
|
|
43
|
+
ワイルドカード中心 (`cloudformation:*` 等)。
|
|
44
|
+
"""
|
|
45
|
+
settings = Settings.from_toml(stage=stage)
|
|
46
|
+
actions = compute_actions(settings)
|
|
47
|
+
if format_ == "json":
|
|
48
|
+
click.echo(json.dumps({"actions": actions}, indent=2))
|
|
49
|
+
else:
|
|
50
|
+
for action in actions:
|
|
51
|
+
click.echo(action)
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
@permissions.command("roles")
|
|
55
|
+
@click.option("--stage", envvar="POCKET_DEPLOY_STAGE", prompt=True)
|
|
56
|
+
@click.option(
|
|
57
|
+
"--account-id",
|
|
58
|
+
help="role の ARN と policy に埋め込む AWS account ID。省略時は"
|
|
59
|
+
" sts:GetCallerIdentity で取得する。",
|
|
60
|
+
)
|
|
61
|
+
def roles(stage: str, account_id: str | None):
|
|
62
|
+
"""pocket が使う IAM role の定義を JSON で出力する。
|
|
63
|
+
|
|
64
|
+
role ごとに名前・ARN・信頼ポリシー・managed policy・inline policy を出す。
|
|
65
|
+
policy 中の CloudFormation 変数 (${AWS::Region} 等) は展開済みなので、
|
|
66
|
+
そのまま IAM の CreateRole / PutRolePolicy に渡せる。`[iam] external_roles
|
|
67
|
+
= true` の stage では、deploy がこの定義と実際の role を突き合わせる。
|
|
68
|
+
"""
|
|
69
|
+
context = Context.from_toml(stage=stage)
|
|
70
|
+
if not context.general:
|
|
71
|
+
raise click.ClickException("[general] が見つかりません")
|
|
72
|
+
region = context.general.region
|
|
73
|
+
account = (
|
|
74
|
+
account_id
|
|
75
|
+
or (boto3.client("sts", region_name=region).get_caller_identity()["Account"])
|
|
76
|
+
)
|
|
77
|
+
specs = stage_role_specs(context, account_id=account)
|
|
78
|
+
output = {
|
|
79
|
+
"stage": stage,
|
|
80
|
+
"region": region,
|
|
81
|
+
"account_id": account,
|
|
82
|
+
"external_roles": context.external_roles,
|
|
83
|
+
"roles": [
|
|
84
|
+
{
|
|
85
|
+
"name": spec.name,
|
|
86
|
+
"arn": f"arn:aws:iam::{account}:role/{spec.name}",
|
|
87
|
+
"assume_role_policy": spec.assume_role_policy,
|
|
88
|
+
"managed_policy_arns": spec.managed_policy_arns,
|
|
89
|
+
"inline_policies": spec.resolved_inline_policies(
|
|
90
|
+
region=region, account_id=account
|
|
91
|
+
),
|
|
92
|
+
}
|
|
93
|
+
for spec in specs
|
|
94
|
+
],
|
|
95
|
+
}
|
|
96
|
+
click.echo(json.dumps(output, indent=2, ensure_ascii=False))
|
{magic_pocket_cli-0.40.0 → magic_pocket_cli-0.41.0}/pocket_cli/resources/aws/builders/__init__.py
RENAMED
|
@@ -27,6 +27,7 @@ def create_builder(
|
|
|
27
27
|
resource_prefix: str,
|
|
28
28
|
state_bucket: str,
|
|
29
29
|
permissions_boundary: str | None = None,
|
|
30
|
+
external_roles: bool = False,
|
|
30
31
|
) -> Builder:
|
|
31
32
|
backend = build_context.backend
|
|
32
33
|
|
|
@@ -44,6 +45,7 @@ def create_builder(
|
|
|
44
45
|
state_bucket=state_bucket,
|
|
45
46
|
compute_type=build_context.compute_type,
|
|
46
47
|
permissions_boundary=permissions_boundary,
|
|
48
|
+
external_roles=external_roles,
|
|
47
49
|
)
|
|
48
50
|
|
|
49
51
|
if backend == "depot":
|
{magic_pocket_cli-0.40.0 → magic_pocket_cli-0.41.0}/pocket_cli/resources/aws/builders/codebuild.py
RENAMED
|
@@ -50,6 +50,7 @@ class CodeBuildBuilder:
|
|
|
50
50
|
state_bucket: str,
|
|
51
51
|
compute_type: str = "BUILD_GENERAL1_MEDIUM",
|
|
52
52
|
permissions_boundary: str | None = None,
|
|
53
|
+
external_roles: bool = False,
|
|
53
54
|
) -> None:
|
|
54
55
|
self.region = region
|
|
55
56
|
self.resource_prefix = resource_prefix
|
|
@@ -58,6 +59,8 @@ class CodeBuildBuilder:
|
|
|
58
59
|
self.permissions_boundary = (
|
|
59
60
|
os.environ.get("CODEBUILD_PERMISSIONS_BOUNDARY") or permissions_boundary
|
|
60
61
|
)
|
|
62
|
+
# [iam] external_roles。role は利用者が事前に作り、pocket は作成も削除もしない
|
|
63
|
+
self.external_roles = external_roles
|
|
61
64
|
|
|
62
65
|
self.codebuild = boto3.client("codebuild", region_name=region)
|
|
63
66
|
self.iam = boto3.client("iam", region_name=region)
|
|
@@ -91,7 +94,8 @@ class CodeBuildBuilder:
|
|
|
91
94
|
role_arn = self._ensure_role(account_id)
|
|
92
95
|
self._ensure_project(platform, role_arn)
|
|
93
96
|
# project の serviceRole を新ロールへ付け替えた後なので、旧名ロールは孤児
|
|
94
|
-
self.
|
|
97
|
+
if not self.external_roles:
|
|
98
|
+
self._delete_role(self._legacy_role_name)
|
|
95
99
|
self._upload_source(dockerfile_path)
|
|
96
100
|
|
|
97
101
|
build_id = self._start_build(
|
|
@@ -106,26 +110,29 @@ class CodeBuildBuilder:
|
|
|
106
110
|
print("CodeBuild ビルド完了")
|
|
107
111
|
|
|
108
112
|
def delete(self) -> None:
|
|
109
|
-
"""CodeBuildプロジェクトとIAMロールを削除"""
|
|
113
|
+
"""CodeBuildプロジェクトとIAMロールを削除 (external_roles の role は残す)"""
|
|
110
114
|
self._delete_project()
|
|
115
|
+
if self.external_roles:
|
|
116
|
+
return
|
|
111
117
|
self._delete_role(self._role_name)
|
|
112
118
|
self._delete_role(self._legacy_role_name)
|
|
113
119
|
|
|
114
120
|
# --- IAM ロール ---
|
|
115
121
|
|
|
116
|
-
def
|
|
117
|
-
return iam_roles.
|
|
118
|
-
self.
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
permissions_boundary=self.permissions_boundary,
|
|
126
|
-
),
|
|
122
|
+
def role_spec(self, account_id: str) -> iam_roles.RoleSpec:
|
|
123
|
+
return iam_roles.codebuild_role(
|
|
124
|
+
name=self._role_name,
|
|
125
|
+
region=self.region,
|
|
126
|
+
account_id=account_id,
|
|
127
|
+
state_bucket=self.state_bucket,
|
|
128
|
+
project_name=self._project_name,
|
|
129
|
+
permissions_boundary=self.permissions_boundary,
|
|
130
|
+
external=self.external_roles,
|
|
127
131
|
)
|
|
128
132
|
|
|
133
|
+
def _ensure_role(self, account_id: str) -> str:
|
|
134
|
+
return iam_roles.ensure_role(self.iam, self.role_spec(account_id))
|
|
135
|
+
|
|
129
136
|
def _delete_role(self, role_name: str) -> None:
|
|
130
137
|
iam_roles.delete_role(self.iam, role_name)
|
|
131
138
|
|
{magic_pocket_cli-0.40.0 → magic_pocket_cli-0.41.0}/pocket_cli/resources/aws/cloudformation.py
RENAMED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
+
import json
|
|
4
|
+
import re
|
|
3
5
|
import time
|
|
4
6
|
from functools import cached_property
|
|
5
7
|
from typing import TYPE_CHECKING, Any
|
|
@@ -436,6 +438,12 @@ class CloudFrontKeysStack(Stack):
|
|
|
436
438
|
)
|
|
437
439
|
|
|
438
440
|
|
|
441
|
+
# deploy_hash route の URL で hash とみなすセグメントの形 (git の short / full hash)。
|
|
442
|
+
DEPLOY_HASH_PATTERN = "[0-9a-f]{7,40}"
|
|
443
|
+
# strip Function が URI から外した hash を載せるリクエストヘッダ (キャッシュキー用)。
|
|
444
|
+
DEPLOY_HASH_HEADER = "x-pocket-deploy-hash"
|
|
445
|
+
|
|
446
|
+
|
|
439
447
|
class CloudFrontStack(Stack):
|
|
440
448
|
context: CloudFrontContext
|
|
441
449
|
template_filename = "cloudfront"
|
|
@@ -627,25 +635,67 @@ class CloudFrontStack(Stack):
|
|
|
627
635
|
return self._reindent(code, 8)
|
|
628
636
|
|
|
629
637
|
def _build_deploy_hash_function_codes(self) -> dict[str, str]:
|
|
630
|
-
"""deploy_hash route 用の hash prefix strip Function コードを生成する
|
|
638
|
+
"""deploy_hash route 用の hash prefix strip Function コードを生成する
|
|
639
|
+
|
|
640
|
+
route の prefix 直下の 1 セグメントが git hash の形 (`DEPLOY_HASH_PATTERN`)
|
|
641
|
+
なら、値を問わず外す。現在の hash だけを外すと、Lambda (新 hash の URL を出す)
|
|
642
|
+
とこの Function の切り替えがずれる間、および旧 hash の HTML を開いたままの
|
|
643
|
+
端末で、存在しないキーを読んで 403 になる (KN1670)。S3 のキーは hash に依存
|
|
644
|
+
しないので、どの hash の URL でも同じオブジェクトを返せる。
|
|
645
|
+
|
|
646
|
+
`DEPLOY_HASH` を git hash の形でない値で上書きしている場合だけ、その値も外す
|
|
647
|
+
(この場合は deploy のたびに Function が更新される)。
|
|
648
|
+
|
|
649
|
+
CloudFront のキャッシュキーは viewer-request で書き換えた後の URI になるため、
|
|
650
|
+
外した hash は `DEPLOY_HASH_HEADER` に載せ、テンプレート側の CachePolicy で
|
|
651
|
+
キャッシュキーに含める。こうしないと全 hash の URL が 1 つの edge キャッシュを
|
|
652
|
+
共有し、deploy しても中身の変わったファイルが古いまま返る。
|
|
653
|
+
"""
|
|
631
654
|
codes: dict[str, str] = {}
|
|
632
655
|
deploy_hash = self.context.deploy_hash
|
|
633
656
|
if not deploy_hash:
|
|
634
657
|
return codes
|
|
658
|
+
segment_condition = "/^%s$/.test(segment)" % DEPLOY_HASH_PATTERN
|
|
659
|
+
if not re.fullmatch(DEPLOY_HASH_PATTERN, deploy_hash):
|
|
660
|
+
segment_condition += " || segment === %s" % json.dumps(deploy_hash)
|
|
661
|
+
env = Environment(
|
|
662
|
+
loader=PackageLoader("pocket_cli"),
|
|
663
|
+
autoescape=select_autoescape(),
|
|
664
|
+
)
|
|
665
|
+
template = env.get_template("cloudformation/cf_function_deploy_hash_strip.js")
|
|
635
666
|
for route in self.context.routes:
|
|
636
667
|
if not route.is_deploy_hash:
|
|
637
668
|
continue
|
|
638
|
-
code = (
|
|
639
|
-
"
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
" return request;\n"
|
|
643
|
-
"}" % deploy_hash
|
|
669
|
+
code = template.render(
|
|
670
|
+
prefix=route.path_pattern.rstrip("*").rstrip("/") + "/",
|
|
671
|
+
hash_header=DEPLOY_HASH_HEADER,
|
|
672
|
+
segment_condition=segment_condition,
|
|
644
673
|
)
|
|
645
674
|
code = self._inject_viewer_preludes(code)
|
|
646
675
|
codes[route.yaml_key] = self._reindent(code, 8)
|
|
647
676
|
return codes
|
|
648
677
|
|
|
678
|
+
def _build_cache_control_function_codes(self) -> dict[str, str]:
|
|
679
|
+
"""versioning route の cache-control を付ける viewer-response Function コード。
|
|
680
|
+
|
|
681
|
+
ResponseHeadersPolicy はステータスを問わずヘッダを付けるため、存在しない
|
|
682
|
+
キーへの 403 にも immutable が付き、ブラウザがエラーを max-age の間保存して
|
|
683
|
+
しまう (KN1670)。
|
|
684
|
+
成功応答 (2xx / 304) だけに付ける。
|
|
685
|
+
"""
|
|
686
|
+
env = Environment(
|
|
687
|
+
loader=PackageLoader("pocket_cli"),
|
|
688
|
+
autoescape=select_autoescape(),
|
|
689
|
+
)
|
|
690
|
+
template = env.get_template("cloudformation/cf_function_cache_control.js")
|
|
691
|
+
codes: dict[str, str] = {}
|
|
692
|
+
for route in self.context.routes:
|
|
693
|
+
if not route.versioning:
|
|
694
|
+
continue
|
|
695
|
+
code = template.render(max_age=route.versioned_max_age)
|
|
696
|
+
codes[route.yaml_key] = self._reindent(code, 8)
|
|
697
|
+
return codes
|
|
698
|
+
|
|
649
699
|
@staticmethod
|
|
650
700
|
def _spa_fallback_uri(route) -> str:
|
|
651
701
|
"""SPA fallback の書き換え先 URI を組み立てる。
|
|
@@ -704,6 +754,7 @@ class CloudFrontStack(Stack):
|
|
|
704
754
|
waf_acl_arn = self._resolve_waf_arn()
|
|
705
755
|
function_codes = self._build_function_codes()
|
|
706
756
|
deploy_hash_function_codes = self._build_deploy_hash_function_codes()
|
|
757
|
+
cache_control_function_codes = self._build_cache_control_function_codes()
|
|
707
758
|
api_host_function_code = ""
|
|
708
759
|
if self.context.has_lambda_route:
|
|
709
760
|
api_host_function_code = self._generate_api_host_function()
|
|
@@ -726,6 +777,8 @@ class CloudFrontStack(Stack):
|
|
|
726
777
|
waf_acl_arn=waf_acl_arn,
|
|
727
778
|
function_codes=function_codes,
|
|
728
779
|
deploy_hash_function_codes=deploy_hash_function_codes,
|
|
780
|
+
cache_control_function_codes=cache_control_function_codes,
|
|
781
|
+
deploy_hash_header=DEPLOY_HASH_HEADER,
|
|
729
782
|
api_host_function_code=api_host_function_code,
|
|
730
783
|
host_redirect_function_code=host_redirect_function_code,
|
|
731
784
|
basic_auth_function_code=basic_auth_function_code,
|
|
@@ -761,9 +814,7 @@ class ContainerStack(Stack):
|
|
|
761
814
|
"rds_security_group_id": self._rds_context.security_group_id,
|
|
762
815
|
"rds_secret_store": "sm",
|
|
763
816
|
"rds_secret_arn": self._rds_context.secret_arn,
|
|
764
|
-
"rds_kms_key_id": None,
|
|
765
817
|
"rds_ssm_param_name": None,
|
|
766
|
-
"rds_ssm_param_arn": None,
|
|
767
818
|
"rds_endpoint": None,
|
|
768
819
|
"rds_port": None,
|
|
769
820
|
"rds_dbname": None,
|
|
@@ -784,30 +835,23 @@ class ContainerStack(Stack):
|
|
|
784
835
|
**base,
|
|
785
836
|
"rds_secret_store": "ssm",
|
|
786
837
|
"rds_secret_arn": None,
|
|
787
|
-
"rds_kms_key_id": None,
|
|
788
838
|
"rds_ssm_param_name": ssm_param_name,
|
|
789
|
-
"rds_ssm_param_arn": (
|
|
790
|
-
"arn:aws:ssm:${AWS::Region}:${AWS::AccountId}:parameter/"
|
|
791
|
-
+ ssm_param_name
|
|
792
|
-
),
|
|
793
839
|
}
|
|
794
840
|
return {
|
|
795
841
|
**base,
|
|
796
842
|
"rds_secret_store": "sm",
|
|
797
843
|
"rds_secret_arn": rds.master_user_secret_arn,
|
|
798
|
-
"rds_kms_key_id": rds.master_user_secret_kms_key_id,
|
|
799
844
|
"rds_ssm_param_name": None,
|
|
800
|
-
"rds_ssm_param_arn": None,
|
|
801
845
|
}
|
|
802
846
|
|
|
803
|
-
def _resolve_dsql(self) -> tuple[str | None, str | None
|
|
804
|
-
"""DSQL
|
|
847
|
+
def _resolve_dsql(self) -> tuple[str | None, str | None]:
|
|
848
|
+
"""DSQL のエンドポイントとリージョンを動的に取得"""
|
|
805
849
|
if self._dsql_context is None:
|
|
806
|
-
return None, None
|
|
850
|
+
return None, None
|
|
807
851
|
from pocket_cli.resources.dsql import Dsql
|
|
808
852
|
|
|
809
853
|
dsql = Dsql(self._dsql_context)
|
|
810
|
-
return dsql.endpoint, self._dsql_context.region
|
|
854
|
+
return dsql.endpoint, self._dsql_context.region
|
|
811
855
|
|
|
812
856
|
@property
|
|
813
857
|
def name(self):
|
|
@@ -841,10 +885,31 @@ class ContainerStack(Stack):
|
|
|
841
885
|
break
|
|
842
886
|
return count
|
|
843
887
|
|
|
888
|
+
@property
|
|
889
|
+
def lambda_role(self) -> iam_roles.RoleSpec:
|
|
890
|
+
return iam_roles.lambda_role(
|
|
891
|
+
self.context, rds=self._rds_context, dsql=self._dsql_context
|
|
892
|
+
)
|
|
893
|
+
|
|
894
|
+
@property
|
|
895
|
+
def scheduler_role(self) -> iam_roles.RoleSpec | None:
|
|
896
|
+
if not (self._scheduler_context and self._scheduler_context.has_schedules):
|
|
897
|
+
return None
|
|
898
|
+
return iam_roles.scheduler_role(
|
|
899
|
+
self._scheduler_context,
|
|
900
|
+
permissions_boundary=self.context.permissions_boundary,
|
|
901
|
+
external=self.context.external_roles,
|
|
902
|
+
)
|
|
903
|
+
|
|
904
|
+
@property
|
|
905
|
+
def role_specs(self) -> list[iam_roles.RoleSpec]:
|
|
906
|
+
"""この stack の Lambda が使う role (external_roles では事前作成が要る)。"""
|
|
907
|
+
return [r for r in (self.lambda_role, self.scheduler_role) if r]
|
|
908
|
+
|
|
844
909
|
@property
|
|
845
910
|
def yaml(self) -> str:
|
|
846
911
|
rds_info = self._resolve_rds()
|
|
847
|
-
dsql_endpoint, dsql_region
|
|
912
|
+
dsql_endpoint, dsql_region = self._resolve_dsql()
|
|
848
913
|
context_dump = self.context.model_dump()
|
|
849
914
|
|
|
850
915
|
# 外部 VPC: zones を動的取得
|
|
@@ -859,37 +924,21 @@ class ContainerStack(Stack):
|
|
|
859
924
|
rds_security_group_id=rds_info.get("rds_security_group_id"),
|
|
860
925
|
rds_secret_store=rds_info.get("rds_secret_store"),
|
|
861
926
|
rds_secret_arn=rds_info.get("rds_secret_arn"),
|
|
862
|
-
rds_kms_key_id=rds_info.get("rds_kms_key_id"),
|
|
863
927
|
rds_ssm_param_name=rds_info.get("rds_ssm_param_name"),
|
|
864
|
-
rds_ssm_param_arn=rds_info.get("rds_ssm_param_arn"),
|
|
865
928
|
rds_endpoint=rds_info.get("rds_endpoint"),
|
|
866
929
|
rds_port=rds_info.get("rds_port"),
|
|
867
930
|
rds_dbname=rds_info.get("rds_dbname"),
|
|
868
931
|
use_rds=bool(rds_info),
|
|
869
932
|
dsql_endpoint=dsql_endpoint,
|
|
870
933
|
dsql_region=dsql_region,
|
|
871
|
-
dsql_cluster_arn=dsql_cluster_arn,
|
|
872
934
|
use_dsql=dsql_endpoint is not None,
|
|
873
935
|
scheduler=(
|
|
874
936
|
self._scheduler_context.model_dump()
|
|
875
937
|
if self._scheduler_context
|
|
876
938
|
else None
|
|
877
939
|
),
|
|
878
|
-
lambda_role=
|
|
879
|
-
|
|
880
|
-
rds_secret_arn=rds_info.get("rds_secret_arn"),
|
|
881
|
-
rds_kms_key_id=rds_info.get("rds_kms_key_id"),
|
|
882
|
-
rds_ssm_param_arn=rds_info.get("rds_ssm_param_arn"),
|
|
883
|
-
dsql_cluster_arn=dsql_cluster_arn if dsql_endpoint else None,
|
|
884
|
-
),
|
|
885
|
-
scheduler_role=(
|
|
886
|
-
iam_roles.scheduler_role(
|
|
887
|
-
self._scheduler_context,
|
|
888
|
-
permissions_boundary=self.context.permissions_boundary,
|
|
889
|
-
)
|
|
890
|
-
if self._scheduler_context
|
|
891
|
-
else None
|
|
892
|
-
),
|
|
940
|
+
lambda_role=self.lambda_role,
|
|
941
|
+
scheduler_role=self.scheduler_role,
|
|
893
942
|
)
|
|
894
943
|
|
|
895
944
|
|