magic-pocket-cli 0.16.0__tar.gz → 0.17.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.16.0 → magic_pocket_cli-0.17.0}/PKG-INFO +2 -2
- {magic_pocket_cli-0.16.0 → magic_pocket_cli-0.17.0}/pocket_cli/cli/neon_cli.py +7 -8
- magic_pocket_cli-0.17.0/pocket_cli/resources/neon.py +39 -0
- {magic_pocket_cli-0.16.0 → magic_pocket_cli-0.17.0}/pyproject.toml +2 -2
- magic_pocket_cli-0.16.0/pocket_cli/resources/neon.py +0 -392
- {magic_pocket_cli-0.16.0 → magic_pocket_cli-0.17.0}/.gitignore +0 -0
- {magic_pocket_cli-0.16.0 → magic_pocket_cli-0.17.0}/pocket_cli/__init__.py +0 -0
- {magic_pocket_cli-0.16.0 → magic_pocket_cli-0.17.0}/pocket_cli/cli/__init__.py +0 -0
- {magic_pocket_cli-0.16.0 → magic_pocket_cli-0.17.0}/pocket_cli/cli/aws_auth.py +0 -0
- {magic_pocket_cli-0.16.0 → magic_pocket_cli-0.17.0}/pocket_cli/cli/awscontainer_cli.py +0 -0
- {magic_pocket_cli-0.16.0 → magic_pocket_cli-0.17.0}/pocket_cli/cli/cloudfront_cli.py +0 -0
- {magic_pocket_cli-0.16.0 → magic_pocket_cli-0.17.0}/pocket_cli/cli/cloudfront_keys_cli.py +0 -0
- {magic_pocket_cli-0.16.0 → magic_pocket_cli-0.17.0}/pocket_cli/cli/cloudfront_waf_cli.py +0 -0
- {magic_pocket_cli-0.16.0 → magic_pocket_cli-0.17.0}/pocket_cli/cli/deploy_cli.py +0 -0
- {magic_pocket_cli-0.16.0 → magic_pocket_cli-0.17.0}/pocket_cli/cli/destroy_cli.py +0 -0
- {magic_pocket_cli-0.16.0 → magic_pocket_cli-0.17.0}/pocket_cli/cli/dsql_cli.py +0 -0
- {magic_pocket_cli-0.16.0 → magic_pocket_cli-0.17.0}/pocket_cli/cli/main_cli.py +0 -0
- {magic_pocket_cli-0.16.0 → magic_pocket_cli-0.17.0}/pocket_cli/cli/migrate_cli.py +0 -0
- {magic_pocket_cli-0.16.0 → magic_pocket_cli-0.17.0}/pocket_cli/cli/permissions_cli.py +0 -0
- {magic_pocket_cli-0.16.0 → magic_pocket_cli-0.17.0}/pocket_cli/cli/rds_cli.py +0 -0
- {magic_pocket_cli-0.16.0 → magic_pocket_cli-0.17.0}/pocket_cli/cli/runtime_config_cli.py +0 -0
- {magic_pocket_cli-0.16.0 → magic_pocket_cli-0.17.0}/pocket_cli/cli/s3_cli.py +0 -0
- {magic_pocket_cli-0.16.0 → magic_pocket_cli-0.17.0}/pocket_cli/cli/status_cli.py +0 -0
- {magic_pocket_cli-0.16.0 → magic_pocket_cli-0.17.0}/pocket_cli/cli/store_url_helper.py +0 -0
- {magic_pocket_cli-0.16.0 → magic_pocket_cli-0.17.0}/pocket_cli/cli/tidb_cli.py +0 -0
- {magic_pocket_cli-0.16.0 → magic_pocket_cli-0.17.0}/pocket_cli/cli/upstash_cli.py +0 -0
- {magic_pocket_cli-0.16.0 → magic_pocket_cli-0.17.0}/pocket_cli/cli/url_helper.py +0 -0
- {magic_pocket_cli-0.16.0 → magic_pocket_cli-0.17.0}/pocket_cli/cli/vpc_cli.py +0 -0
- {magic_pocket_cli-0.16.0 → magic_pocket_cli-0.17.0}/pocket_cli/cli/waf_cli.py +0 -0
- {magic_pocket_cli-0.16.0 → magic_pocket_cli-0.17.0}/pocket_cli/django_cli.py +0 -0
- {magic_pocket_cli-0.16.0 → magic_pocket_cli-0.17.0}/pocket_cli/mediator.py +0 -0
- {magic_pocket_cli-0.16.0 → magic_pocket_cli-0.17.0}/pocket_cli/resources/__init__.py +0 -0
- {magic_pocket_cli-0.16.0 → magic_pocket_cli-0.17.0}/pocket_cli/resources/aws/__init__.py +0 -0
- {magic_pocket_cli-0.16.0 → magic_pocket_cli-0.17.0}/pocket_cli/resources/aws/builders/__init__.py +0 -0
- {magic_pocket_cli-0.16.0 → magic_pocket_cli-0.17.0}/pocket_cli/resources/aws/builders/codebuild.py +0 -0
- {magic_pocket_cli-0.16.0 → magic_pocket_cli-0.17.0}/pocket_cli/resources/aws/builders/depot.py +0 -0
- {magic_pocket_cli-0.16.0 → magic_pocket_cli-0.17.0}/pocket_cli/resources/aws/builders/docker.py +0 -0
- {magic_pocket_cli-0.16.0 → magic_pocket_cli-0.17.0}/pocket_cli/resources/aws/builders/dockerignore.py +0 -0
- {magic_pocket_cli-0.16.0 → magic_pocket_cli-0.17.0}/pocket_cli/resources/aws/cloudformation.py +0 -0
- {magic_pocket_cli-0.16.0 → magic_pocket_cli-0.17.0}/pocket_cli/resources/aws/ecr.py +0 -0
- {magic_pocket_cli-0.16.0 → magic_pocket_cli-0.17.0}/pocket_cli/resources/aws/efs.py +0 -0
- {magic_pocket_cli-0.16.0 → magic_pocket_cli-0.17.0}/pocket_cli/resources/aws/lambdahandler.py +0 -0
- {magic_pocket_cli-0.16.0 → magic_pocket_cli-0.17.0}/pocket_cli/resources/aws/s3_utils.py +0 -0
- {magic_pocket_cli-0.16.0 → magic_pocket_cli-0.17.0}/pocket_cli/resources/aws/state.py +0 -0
- {magic_pocket_cli-0.16.0 → magic_pocket_cli-0.17.0}/pocket_cli/resources/awscontainer.py +0 -0
- {magic_pocket_cli-0.16.0 → magic_pocket_cli-0.17.0}/pocket_cli/resources/cloudfront.py +0 -0
- {magic_pocket_cli-0.16.0 → magic_pocket_cli-0.17.0}/pocket_cli/resources/cloudfront_acm.py +0 -0
- {magic_pocket_cli-0.16.0 → magic_pocket_cli-0.17.0}/pocket_cli/resources/cloudfront_keys.py +0 -0
- {magic_pocket_cli-0.16.0 → magic_pocket_cli-0.17.0}/pocket_cli/resources/cloudfront_waf.py +0 -0
- {magic_pocket_cli-0.16.0 → magic_pocket_cli-0.17.0}/pocket_cli/resources/dsql.py +0 -0
- {magic_pocket_cli-0.16.0 → magic_pocket_cli-0.17.0}/pocket_cli/resources/rds.py +0 -0
- {magic_pocket_cli-0.16.0 → magic_pocket_cli-0.17.0}/pocket_cli/resources/s3.py +0 -0
- {magic_pocket_cli-0.16.0 → magic_pocket_cli-0.17.0}/pocket_cli/resources/tidb.py +0 -0
- {magic_pocket_cli-0.16.0 → magic_pocket_cli-0.17.0}/pocket_cli/resources/upstash.py +0 -0
- {magic_pocket_cli-0.16.0 → magic_pocket_cli-0.17.0}/pocket_cli/resources/vpc.py +0 -0
- {magic_pocket_cli-0.16.0 → magic_pocket_cli-0.17.0}/pocket_cli/templates/cloudformation/awscontainer.yaml +0 -0
- {magic_pocket_cli-0.16.0 → magic_pocket_cli-0.17.0}/pocket_cli/templates/cloudformation/cf_function_api_host.js +0 -0
- {magic_pocket_cli-0.16.0 → magic_pocket_cli-0.17.0}/pocket_cli/templates/cloudformation/cf_function_host_redirect_prelude.js +0 -0
- {magic_pocket_cli-0.16.0 → magic_pocket_cli-0.17.0}/pocket_cli/templates/cloudformation/cf_function_spa_auth.js +0 -0
- {magic_pocket_cli-0.16.0 → magic_pocket_cli-0.17.0}/pocket_cli/templates/cloudformation/cf_function_spa_fallback.js +0 -0
- {magic_pocket_cli-0.16.0 → magic_pocket_cli-0.17.0}/pocket_cli/templates/cloudformation/cloudfront.yaml +0 -0
- {magic_pocket_cli-0.16.0 → magic_pocket_cli-0.17.0}/pocket_cli/templates/cloudformation/cloudfront_acm.yaml +0 -0
- {magic_pocket_cli-0.16.0 → magic_pocket_cli-0.17.0}/pocket_cli/templates/cloudformation/cloudfront_keys.yaml +0 -0
- {magic_pocket_cli-0.16.0 → magic_pocket_cli-0.17.0}/pocket_cli/templates/cloudformation/cloudfront_waf.yaml +0 -0
- {magic_pocket_cli-0.16.0 → magic_pocket_cli-0.17.0}/pocket_cli/templates/cloudformation/vpc.yaml +0 -0
- {magic_pocket_cli-0.16.0 → magic_pocket_cli-0.17.0}/pocket_cli/templates/init/django-dotenv.env +0 -0
- {magic_pocket_cli-0.16.0 → magic_pocket_cli-0.17.0}/pocket_cli/templates/init/django-settings.py +0 -0
- {magic_pocket_cli-0.16.0 → magic_pocket_cli-0.17.0}/pocket_cli/templates/init/pocket.Dockerfile +0 -0
- {magic_pocket_cli-0.16.0 → magic_pocket_cli-0.17.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.
|
|
3
|
+
Version: 0.17.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.17.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
|
|
@@ -6,7 +6,7 @@ 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
8
|
from pocket_cli.cli.url_helper import run_get_url
|
|
9
|
-
from pocket_cli.resources.neon import Neon
|
|
9
|
+
from pocket_cli.resources.neon import Neon, ensure_url_for_context
|
|
10
10
|
|
|
11
11
|
|
|
12
12
|
@click.group()
|
|
@@ -83,13 +83,12 @@ def store_url(stage, key, force):
|
|
|
83
83
|
"""
|
|
84
84
|
|
|
85
85
|
def ensure_and_compute_url(context):
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
return Neon(context.neon).database_url
|
|
86
|
+
if not context.neon:
|
|
87
|
+
raise click.ClickException(
|
|
88
|
+
"neon が pocket.toml に宣言されていません (store-url 不可)"
|
|
89
|
+
)
|
|
90
|
+
# ensure + URL 算出は runtime package の共有ヘルパに一本化 (公開 API と同一経路)
|
|
91
|
+
return ensure_url_for_context(context.neon)
|
|
93
92
|
|
|
94
93
|
run_store_url(
|
|
95
94
|
stage=stage,
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
"""Neon リソースの後方互換 re-export。
|
|
2
|
+
|
|
3
|
+
実装は runtime package の :mod:`pocket.provisioning.neon` に移設済み
|
|
4
|
+
(20260712 feedback: 外部 provisioner が pocket.toml / pocket_cli なしで
|
|
5
|
+
ensure + URL 算出を import 共有できるようにするため)。CLI 側の既存 import
|
|
6
|
+
(`from pocket_cli.resources.neon import Neon` 等) はここで再エクスポートして維持する。
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
from pocket.provisioning.neon import (
|
|
12
|
+
Branch,
|
|
13
|
+
Database,
|
|
14
|
+
Endpoint,
|
|
15
|
+
Neon,
|
|
16
|
+
NeonApi,
|
|
17
|
+
NeonNotFound,
|
|
18
|
+
NeonResourceIsNotReady,
|
|
19
|
+
Project,
|
|
20
|
+
ResourceType,
|
|
21
|
+
Role,
|
|
22
|
+
ensure_and_compute_url,
|
|
23
|
+
ensure_url_for_context,
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
__all__ = [
|
|
27
|
+
"Branch",
|
|
28
|
+
"Database",
|
|
29
|
+
"Endpoint",
|
|
30
|
+
"Neon",
|
|
31
|
+
"NeonApi",
|
|
32
|
+
"NeonNotFound",
|
|
33
|
+
"NeonResourceIsNotReady",
|
|
34
|
+
"Project",
|
|
35
|
+
"ResourceType",
|
|
36
|
+
"Role",
|
|
37
|
+
"ensure_and_compute_url",
|
|
38
|
+
"ensure_url_for_context",
|
|
39
|
+
]
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "magic-pocket-cli"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.17.0"
|
|
4
4
|
description = "CLI and deploy tools for magic-pocket."
|
|
5
5
|
requires-python = ">=3.10"
|
|
6
6
|
dependencies = [
|
|
7
|
-
"magic-pocket>=0.
|
|
7
|
+
"magic-pocket>=0.17.0",
|
|
8
8
|
"click>=8.1.7",
|
|
9
9
|
"jinja2>=3.1.3",
|
|
10
10
|
"python-on-whales>=0.68.0",
|
|
@@ -1,392 +0,0 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
|
|
3
|
-
import json
|
|
4
|
-
import logging
|
|
5
|
-
import os
|
|
6
|
-
import re
|
|
7
|
-
import time
|
|
8
|
-
from functools import cached_property
|
|
9
|
-
from typing import TYPE_CHECKING, Literal
|
|
10
|
-
|
|
11
|
-
import requests
|
|
12
|
-
from pydantic import BaseModel
|
|
13
|
-
|
|
14
|
-
from pocket.resources.base import ResourceStatus
|
|
15
|
-
|
|
16
|
-
if TYPE_CHECKING:
|
|
17
|
-
from pocket.context import NeonContext
|
|
18
|
-
|
|
19
|
-
logging.basicConfig()
|
|
20
|
-
logger = logging.getLogger(__name__)
|
|
21
|
-
logger.setLevel(level=os.getenv("POCKET_LOGGER_LEVEL", "WARNING").upper())
|
|
22
|
-
|
|
23
|
-
ResourceType = Literal["branches", "databases", "endpoints", "roles"]
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
class NeonResourceIsNotReady(Exception):
|
|
27
|
-
pass
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
class NeonNotFound(Exception):
|
|
31
|
-
"""Neon API が 404 を返したことを示す例外。
|
|
32
|
-
|
|
33
|
-
`branch` / `role` などの個別取得で対象が存在しない場合にこの例外を投げる。
|
|
34
|
-
呼び出し側は存在しないことを明示的に判定できる。
|
|
35
|
-
"""
|
|
36
|
-
|
|
37
|
-
pass
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
class Project(BaseModel):
|
|
41
|
-
id: str
|
|
42
|
-
name: str
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
class Branch(BaseModel):
|
|
46
|
-
id: str
|
|
47
|
-
name: str
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
class Database(BaseModel):
|
|
51
|
-
name: str
|
|
52
|
-
owner_name: str
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
class Endpoint(BaseModel):
|
|
56
|
-
id: str
|
|
57
|
-
host: str
|
|
58
|
-
autoscaling_limit_min_cu: float
|
|
59
|
-
autoscaling_limit_max_cu: float
|
|
60
|
-
type: Literal["read_write", "read_only"]
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
class Role(BaseModel):
|
|
64
|
-
name: str
|
|
65
|
-
password: str | None = None
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
class NeonApi:
|
|
69
|
-
endpoint = "https://console.neon.tech/api/v2/"
|
|
70
|
-
|
|
71
|
-
def __init__(self, key) -> None:
|
|
72
|
-
self.key = key
|
|
73
|
-
|
|
74
|
-
@property
|
|
75
|
-
def header(self):
|
|
76
|
-
return {
|
|
77
|
-
"Accept": "application/json",
|
|
78
|
-
"Authorization": "Bearer %s" % self.key,
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
def get(self, path):
|
|
82
|
-
logger.info("GET %s" % self.endpoint + path)
|
|
83
|
-
res = requests.get(self.endpoint + path, headers=self.header, timeout=30)
|
|
84
|
-
logger.debug(res.status_code)
|
|
85
|
-
logger.debug(json.dumps(res.json(), indent=2))
|
|
86
|
-
if 200 <= res.status_code < 300:
|
|
87
|
-
return res
|
|
88
|
-
if res.status_code == 404:
|
|
89
|
-
raise NeonNotFound("%s: %s" % (res.status_code, res.json()["message"]))
|
|
90
|
-
if res.status_code == 401:
|
|
91
|
-
print("Used API key: %s" % (self.key[:5] + "..." + self.key[-5:]))
|
|
92
|
-
print("API key length: %s" % len(self.key))
|
|
93
|
-
raise Exception("%s: %s" % (res.status_code, res.json()["message"]))
|
|
94
|
-
|
|
95
|
-
def post(self, path, data=None):
|
|
96
|
-
logger.warning("POST %s" % self.endpoint + path)
|
|
97
|
-
logger.debug(json.dumps(data, indent=2))
|
|
98
|
-
res = requests.post(
|
|
99
|
-
self.endpoint + path, headers=self.header, json=data, timeout=30
|
|
100
|
-
)
|
|
101
|
-
logger.debug(res.status_code)
|
|
102
|
-
logger.debug(json.dumps(res.json(), indent=2))
|
|
103
|
-
if 200 <= res.status_code < 300:
|
|
104
|
-
time.sleep(2)
|
|
105
|
-
return res
|
|
106
|
-
if res.status_code == 401:
|
|
107
|
-
print("Used API key: %s" % (self.key[:5] + "..." + self.key[-5:]))
|
|
108
|
-
print("API key length: %s" % len(self.key))
|
|
109
|
-
raise Exception("%s: %s" % (res.status_code, res.json()["message"]))
|
|
110
|
-
|
|
111
|
-
def delete(self, path, data=None):
|
|
112
|
-
logger.warning("DELETE %s" % self.endpoint + path)
|
|
113
|
-
logger.debug(json.dumps(data, indent=2))
|
|
114
|
-
res = requests.delete(
|
|
115
|
-
self.endpoint + path, headers=self.header, json=data, timeout=30
|
|
116
|
-
)
|
|
117
|
-
logger.debug(res.status_code)
|
|
118
|
-
logger.debug(json.dumps(res.json(), indent=2))
|
|
119
|
-
if 200 <= res.status_code < 300:
|
|
120
|
-
time.sleep(2)
|
|
121
|
-
return res
|
|
122
|
-
raise Exception("%s: %s" % (res.status_code, res.json()["message"]))
|
|
123
|
-
|
|
124
|
-
def projects_url(self):
|
|
125
|
-
return self.endpoint + "projects"
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
class Neon:
|
|
129
|
-
context: NeonContext
|
|
130
|
-
|
|
131
|
-
def __init__(self, context: NeonContext) -> None:
|
|
132
|
-
self.context = context
|
|
133
|
-
|
|
134
|
-
def get_resource_path(self, resource_type: ResourceType) -> str:
|
|
135
|
-
requirements = {
|
|
136
|
-
"branches": ["project"],
|
|
137
|
-
"databases": ["project", "branch"],
|
|
138
|
-
"endpoints": ["project"],
|
|
139
|
-
"roles": ["project", "branch"],
|
|
140
|
-
}
|
|
141
|
-
path_templates = {
|
|
142
|
-
"branches": "projects/%(project_id)s/branches",
|
|
143
|
-
"databases": "projects/%(project_id)s/branches/%(branch_id)s/databases",
|
|
144
|
-
"endpoints": "projects/%(project_id)s/endpoints",
|
|
145
|
-
"roles": "projects/%(project_id)s/branches/%(branch_id)s/roles",
|
|
146
|
-
}
|
|
147
|
-
path_context = {}
|
|
148
|
-
for requirement in requirements[resource_type]:
|
|
149
|
-
if not getattr(self, requirement):
|
|
150
|
-
raise Exception("%s not found" % requirement)
|
|
151
|
-
path_context[requirement + "_id"] = getattr(self, requirement).id
|
|
152
|
-
return path_templates[resource_type] % path_context
|
|
153
|
-
|
|
154
|
-
def construct_path(
|
|
155
|
-
self, resource_type: ResourceType, resource_id: str | None = None
|
|
156
|
-
):
|
|
157
|
-
path = self.get_resource_path(resource_type)
|
|
158
|
-
if resource_id:
|
|
159
|
-
path += "/" + resource_id
|
|
160
|
-
return path
|
|
161
|
-
|
|
162
|
-
def get(self, resource_type: ResourceType, resource_id: str | None = None):
|
|
163
|
-
path = self.construct_path(resource_type, resource_id)
|
|
164
|
-
return self.api.get(path)
|
|
165
|
-
|
|
166
|
-
def post(
|
|
167
|
-
self, resource_type: ResourceType, resource_id: str | None = None, data=None
|
|
168
|
-
):
|
|
169
|
-
path = self.construct_path(resource_type, resource_id)
|
|
170
|
-
return self.api.post(path, data)
|
|
171
|
-
|
|
172
|
-
def delete(
|
|
173
|
-
self, resource_type: ResourceType, resource_id: str | None = None, data=None
|
|
174
|
-
):
|
|
175
|
-
path = self.construct_path(resource_type, resource_id)
|
|
176
|
-
return self.api.delete(path, data)
|
|
177
|
-
|
|
178
|
-
@property
|
|
179
|
-
def api(self):
|
|
180
|
-
return NeonApi(self.context.api_key)
|
|
181
|
-
|
|
182
|
-
@cached_property
|
|
183
|
-
def role(self) -> Role | None:
|
|
184
|
-
if not self.branch:
|
|
185
|
-
return None
|
|
186
|
-
try:
|
|
187
|
-
res = self.get("roles", self.context.role_name)
|
|
188
|
-
except NeonNotFound:
|
|
189
|
-
return None
|
|
190
|
-
return Role(**res.json()["role"])
|
|
191
|
-
|
|
192
|
-
@cached_property
|
|
193
|
-
def project(self) -> Project:
|
|
194
|
-
"""project_nameからNeonプロジェクトを解決する。
|
|
195
|
-
|
|
196
|
-
組織キー: GET /projects で一覧取得し name で検索。
|
|
197
|
-
プロジェクトスコープキー: エラーから project_id を取得し直接アクセス。
|
|
198
|
-
"""
|
|
199
|
-
res = requests.get(
|
|
200
|
-
self.api.endpoint + "projects", headers=self.api.header, timeout=30
|
|
201
|
-
)
|
|
202
|
-
if 200 <= res.status_code < 300:
|
|
203
|
-
for p in res.json().get("projects", []):
|
|
204
|
-
if p["name"] == self.context.project_name:
|
|
205
|
-
return Project(**p)
|
|
206
|
-
raise ValueError(f"Neon project '{self.context.project_name}' not found")
|
|
207
|
-
|
|
208
|
-
# プロジェクトスコープキー: エラーから project_id をパース
|
|
209
|
-
message = res.json().get("message", "")
|
|
210
|
-
match = re.search(r'subject_project_id:"([^"]+)"', message)
|
|
211
|
-
if not match:
|
|
212
|
-
raise ValueError(
|
|
213
|
-
f"Failed to resolve Neon project: {res.status_code}: {message}"
|
|
214
|
-
)
|
|
215
|
-
project_id = match.group(1)
|
|
216
|
-
project_res = self.api.get(f"projects/{project_id}")
|
|
217
|
-
project_data = project_res.json()["project"]
|
|
218
|
-
if project_data["name"] != self.context.project_name:
|
|
219
|
-
raise ValueError(
|
|
220
|
-
f"Neon project name mismatch: "
|
|
221
|
-
f"config='{self.context.project_name}', "
|
|
222
|
-
f"actual='{project_data['name']}'"
|
|
223
|
-
)
|
|
224
|
-
return Project(id=project_data["id"], name=project_data["name"])
|
|
225
|
-
|
|
226
|
-
@cached_property
|
|
227
|
-
def branch(self) -> Branch | None:
|
|
228
|
-
if self.project:
|
|
229
|
-
for branch in self.get("branches").json()["branches"]:
|
|
230
|
-
if branch["name"] == self.context.branch_name:
|
|
231
|
-
return Branch(**branch)
|
|
232
|
-
|
|
233
|
-
@cached_property
|
|
234
|
-
def parent_branch(self) -> Branch | None:
|
|
235
|
-
"""branch を新規作成する際の親ブランチ。
|
|
236
|
-
|
|
237
|
-
context.parent_branch_name が未指定なら None (= create_branch が parent_id を
|
|
238
|
-
送らず Neon の default ブランチから分岐)。指定があるのに project 内に該当
|
|
239
|
-
ブランチが無い場合はエラー (黙って default 分岐すると事故になるため)。
|
|
240
|
-
"""
|
|
241
|
-
if not self.context.parent_branch_name:
|
|
242
|
-
return None
|
|
243
|
-
if self.project:
|
|
244
|
-
for branch in self.get("branches").json()["branches"]:
|
|
245
|
-
if branch["name"] == self.context.parent_branch_name:
|
|
246
|
-
return Branch(**branch)
|
|
247
|
-
raise ValueError(
|
|
248
|
-
f"Neon parent branch '{self.context.parent_branch_name}' not found "
|
|
249
|
-
f"in project '{self.context.project_name}'"
|
|
250
|
-
)
|
|
251
|
-
|
|
252
|
-
@cached_property
|
|
253
|
-
def database(self) -> Database | None:
|
|
254
|
-
if self.branch:
|
|
255
|
-
for database in self.get("databases").json()["databases"]:
|
|
256
|
-
if database["name"] == self.context.name:
|
|
257
|
-
return Database(**database)
|
|
258
|
-
|
|
259
|
-
@cached_property
|
|
260
|
-
def endpoint(self) -> Endpoint | None:
|
|
261
|
-
if self.branch:
|
|
262
|
-
for endpoint in self.get("endpoints").json()["endpoints"]:
|
|
263
|
-
if endpoint["branch_id"] == self.branch.id:
|
|
264
|
-
return Endpoint(**endpoint)
|
|
265
|
-
|
|
266
|
-
@property
|
|
267
|
-
def database_url(self):
|
|
268
|
-
if self.role is None or self.endpoint is None:
|
|
269
|
-
raise NeonResourceIsNotReady("Create role and endpoint first")
|
|
270
|
-
if self.role.password is None:
|
|
271
|
-
self.set_role_password()
|
|
272
|
-
return "postgres://%s:%s@%s:5432/%s?sslmode=require" % (
|
|
273
|
-
self.context.role_name,
|
|
274
|
-
self.role.password,
|
|
275
|
-
self.endpoint.host,
|
|
276
|
-
self.context.name,
|
|
277
|
-
)
|
|
278
|
-
|
|
279
|
-
@property
|
|
280
|
-
def status(self) -> ResourceStatus:
|
|
281
|
-
# provisioning="command" の Neon は get_resources で除外されるため、ここに
|
|
282
|
-
# 到達するのは deploy が Neon を管理する provisioning="deploy" の場合のみ。
|
|
283
|
-
if self.working:
|
|
284
|
-
return "COMPLETED"
|
|
285
|
-
return "NOEXIST"
|
|
286
|
-
|
|
287
|
-
@property
|
|
288
|
-
def working(self):
|
|
289
|
-
check = [self.branch, self.database, self.endpoint, self.role]
|
|
290
|
-
logger.info(str(check))
|
|
291
|
-
return all(check)
|
|
292
|
-
|
|
293
|
-
@property
|
|
294
|
-
def description(self):
|
|
295
|
-
return "Create Neon branch, database, role, and endpoint"
|
|
296
|
-
|
|
297
|
-
def create_new(self):
|
|
298
|
-
self.create()
|
|
299
|
-
self.reset_database()
|
|
300
|
-
|
|
301
|
-
def state_info(self):
|
|
302
|
-
return {
|
|
303
|
-
"neon": {
|
|
304
|
-
"project_name": self.context.project_name,
|
|
305
|
-
"branch_name": self.context.branch_name,
|
|
306
|
-
}
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
def deploy_init(self):
|
|
310
|
-
pass
|
|
311
|
-
|
|
312
|
-
def create(self):
|
|
313
|
-
# branch が既に存在するなら (Neon project 作成時に自動生成される default main
|
|
314
|
-
# を含む) その上に role/database を ensure するだけにし branch 作成はスキップ。
|
|
315
|
-
# parent_branch の解決も branch 不在時のみ行う (存在するのに親を要求して誤って
|
|
316
|
-
# ValueError にしないため)。これで default main を使う stage の初回 deploy が
|
|
317
|
-
# 409 (branch already exists) にならず、既存 branch への db/role bootstrap も
|
|
318
|
-
# deploy で完結する。
|
|
319
|
-
if self.branch is None:
|
|
320
|
-
# parent_branch_name 指定時はその親から分岐。未指定なら parent_branch=None
|
|
321
|
-
# で従来通り Neon default ブランチから分岐する。
|
|
322
|
-
self.create_branch(self.parent_branch)
|
|
323
|
-
self.ensure_role()
|
|
324
|
-
self.ensure_database()
|
|
325
|
-
|
|
326
|
-
def create_branch(self, base_branch: Branch | None = None):
|
|
327
|
-
# 冪等化: context.branch_name の branch が既に存在するなら POST /branches は
|
|
328
|
-
# 409 (branch already exists) になるため作成をスキップする。branch_out /
|
|
329
|
-
# store_url の呼び出し側も branch 不在を確認済だが、直接呼ばれた場合の防御。
|
|
330
|
-
if self.branch is not None:
|
|
331
|
-
return
|
|
332
|
-
# branch/endpoint の cached_property を無効化し、branch 作成後の再取得を
|
|
333
|
-
# 強制する。endpoint はこの経路で一度も access していない (cache 未生成) ため
|
|
334
|
-
# 素の `del` は AttributeError になる。access 有無に依らず安全な pop で消す。
|
|
335
|
-
self.__dict__.pop("branch", None)
|
|
336
|
-
self.__dict__.pop("endpoint", None)
|
|
337
|
-
data = {
|
|
338
|
-
"branch": {
|
|
339
|
-
"name": self.context.branch_name,
|
|
340
|
-
},
|
|
341
|
-
"endpoints": [{"type": "read_write"}],
|
|
342
|
-
}
|
|
343
|
-
if base_branch:
|
|
344
|
-
data["branch"]["parent_id"] = base_branch.id
|
|
345
|
-
self.post("branches", data=data)
|
|
346
|
-
|
|
347
|
-
def delete_branch(self):
|
|
348
|
-
if not self.endpoint or not self.branch:
|
|
349
|
-
raise Exception("Branch or endpoint not found. Something is wrong.")
|
|
350
|
-
self.delete("endpoints", self.endpoint.id)
|
|
351
|
-
self.delete("branches", self.branch.id)
|
|
352
|
-
|
|
353
|
-
def ensure_database(self):
|
|
354
|
-
if self.database is None:
|
|
355
|
-
self.create_database()
|
|
356
|
-
|
|
357
|
-
def create_database(self):
|
|
358
|
-
if self.database is None:
|
|
359
|
-
del self.database
|
|
360
|
-
data = {
|
|
361
|
-
"database": {
|
|
362
|
-
"name": self.context.name,
|
|
363
|
-
"owner_name": self.context.role_name,
|
|
364
|
-
}
|
|
365
|
-
}
|
|
366
|
-
self.post("databases", data=data)
|
|
367
|
-
|
|
368
|
-
def reset_database(self):
|
|
369
|
-
if self.database:
|
|
370
|
-
self.delete("databases", self.context.name)
|
|
371
|
-
self.create_database()
|
|
372
|
-
|
|
373
|
-
def create_role(self):
|
|
374
|
-
if self.role is None:
|
|
375
|
-
del self.role
|
|
376
|
-
data = {"role": {"name": self.context.role_name}}
|
|
377
|
-
self.post("roles", data=data)
|
|
378
|
-
|
|
379
|
-
def set_role_password(self):
|
|
380
|
-
if self.role is None:
|
|
381
|
-
raise Exception("Create role first")
|
|
382
|
-
if self.role.password is None:
|
|
383
|
-
self.role.password = self.get(
|
|
384
|
-
"roles", self.role.name + "/reveal_password"
|
|
385
|
-
).json()["password"]
|
|
386
|
-
|
|
387
|
-
def ensure_role(self):
|
|
388
|
-
if self.role is None:
|
|
389
|
-
del self.role
|
|
390
|
-
data = {"role": {"name": self.context.role_name}}
|
|
391
|
-
self.post("roles", data=data)
|
|
392
|
-
self.set_role_password()
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{magic_pocket_cli-0.16.0 → magic_pocket_cli-0.17.0}/pocket_cli/resources/aws/builders/__init__.py
RENAMED
|
File without changes
|
{magic_pocket_cli-0.16.0 → magic_pocket_cli-0.17.0}/pocket_cli/resources/aws/builders/codebuild.py
RENAMED
|
File without changes
|
{magic_pocket_cli-0.16.0 → magic_pocket_cli-0.17.0}/pocket_cli/resources/aws/builders/depot.py
RENAMED
|
File without changes
|
{magic_pocket_cli-0.16.0 → magic_pocket_cli-0.17.0}/pocket_cli/resources/aws/builders/docker.py
RENAMED
|
File without changes
|
|
File without changes
|
{magic_pocket_cli-0.16.0 → magic_pocket_cli-0.17.0}/pocket_cli/resources/aws/cloudformation.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{magic_pocket_cli-0.16.0 → magic_pocket_cli-0.17.0}/pocket_cli/resources/aws/lambdahandler.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{magic_pocket_cli-0.16.0 → magic_pocket_cli-0.17.0}/pocket_cli/templates/cloudformation/vpc.yaml
RENAMED
|
File without changes
|
{magic_pocket_cli-0.16.0 → magic_pocket_cli-0.17.0}/pocket_cli/templates/init/django-dotenv.env
RENAMED
|
File without changes
|
{magic_pocket_cli-0.16.0 → magic_pocket_cli-0.17.0}/pocket_cli/templates/init/django-settings.py
RENAMED
|
File without changes
|
{magic_pocket_cli-0.16.0 → magic_pocket_cli-0.17.0}/pocket_cli/templates/init/pocket.Dockerfile
RENAMED
|
File without changes
|
{magic_pocket_cli-0.16.0 → magic_pocket_cli-0.17.0}/pocket_cli/templates/init/pocket_simple.toml
RENAMED
|
File without changes
|