dbt-platform-helper 13.2.0__py3-none-any.whl → 13.3.0__py3-none-any.whl
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.
Potentially problematic release.
This version of dbt-platform-helper might be problematic. Click here for more details.
- dbt_platform_helper/commands/codebase.py +10 -4
- dbt_platform_helper/commands/config.py +12 -314
- dbt_platform_helper/commands/copilot.py +10 -6
- dbt_platform_helper/commands/database.py +17 -9
- dbt_platform_helper/commands/environment.py +2 -3
- dbt_platform_helper/domain/codebase.py +7 -7
- dbt_platform_helper/domain/config.py +345 -0
- dbt_platform_helper/domain/copilot.py +155 -157
- dbt_platform_helper/domain/versioning.py +48 -7
- dbt_platform_helper/providers/aws/__init__.py +0 -0
- dbt_platform_helper/providers/aws/exceptions.py +10 -0
- dbt_platform_helper/providers/aws/sso_auth.py +61 -0
- dbt_platform_helper/providers/config.py +2 -1
- dbt_platform_helper/providers/config_validator.py +15 -13
- dbt_platform_helper/providers/io.py +2 -2
- dbt_platform_helper/providers/parameter_store.py +47 -0
- dbt_platform_helper/providers/platform_config_schema.py +17 -0
- dbt_platform_helper/providers/semantic_version.py +15 -88
- dbt_platform_helper/providers/terraform_manifest.py +1 -0
- dbt_platform_helper/providers/version.py +82 -24
- dbt_platform_helper/providers/version_status.py +80 -0
- dbt_platform_helper/utils/aws.py +0 -141
- dbt_platform_helper/utils/git.py +3 -1
- dbt_platform_helper/utils/tool_versioning.py +0 -84
- {dbt_platform_helper-13.2.0.dist-info → dbt_platform_helper-13.3.0.dist-info}/METADATA +2 -2
- {dbt_platform_helper-13.2.0.dist-info → dbt_platform_helper-13.3.0.dist-info}/RECORD +30 -30
- {dbt_platform_helper-13.2.0.dist-info → dbt_platform_helper-13.3.0.dist-info}/WHEEL +1 -1
- platform_helper.py +1 -1
- dbt_platform_helper/templates/svc/manifest-backend.yml +0 -69
- dbt_platform_helper/templates/svc/manifest-public.yml +0 -109
- dbt_platform_helper/utils/cloudfoundry.py +0 -14
- dbt_platform_helper/utils/files.py +0 -53
- dbt_platform_helper/utils/manifests.py +0 -18
- {dbt_platform_helper-13.2.0.dist-info → dbt_platform_helper-13.3.0.dist-info}/LICENSE +0 -0
- {dbt_platform_helper-13.2.0.dist-info → dbt_platform_helper-13.3.0.dist-info}/entry_points.txt +0 -0
|
@@ -5,19 +5,19 @@ import json
|
|
|
5
5
|
from pathlib import Path
|
|
6
6
|
from pathlib import PosixPath
|
|
7
7
|
|
|
8
|
-
import
|
|
9
|
-
import
|
|
10
|
-
from schema import SchemaError
|
|
8
|
+
import botocore
|
|
9
|
+
import botocore.errorfactory
|
|
11
10
|
|
|
12
11
|
from dbt_platform_helper.constants import PLATFORM_CONFIG_FILE
|
|
13
12
|
from dbt_platform_helper.domain.copilot_environment import CopilotTemplating
|
|
14
13
|
from dbt_platform_helper.providers.config import ConfigProvider
|
|
15
14
|
from dbt_platform_helper.providers.files import FileProvider
|
|
15
|
+
from dbt_platform_helper.providers.io import ClickIOProvider
|
|
16
16
|
from dbt_platform_helper.providers.kms import KMSProvider
|
|
17
|
+
from dbt_platform_helper.providers.parameter_store import ParameterStore
|
|
18
|
+
from dbt_platform_helper.providers.yaml_file import YamlFileProvider
|
|
17
19
|
from dbt_platform_helper.utils.application import get_application_name
|
|
18
20
|
from dbt_platform_helper.utils.application import load_application
|
|
19
|
-
from dbt_platform_helper.utils.aws import get_aws_session_or_abort
|
|
20
|
-
from dbt_platform_helper.utils.files import generate_override_files
|
|
21
21
|
from dbt_platform_helper.utils.template import ADDON_TEMPLATE_MAP
|
|
22
22
|
from dbt_platform_helper.utils.template import camel_case
|
|
23
23
|
from dbt_platform_helper.utils.template import setup_templates
|
|
@@ -28,9 +28,6 @@ class Copilot:
|
|
|
28
28
|
|
|
29
29
|
PACKAGE_DIR = Path(__file__).resolve().parent.parent
|
|
30
30
|
|
|
31
|
-
# TODO Remove and test
|
|
32
|
-
WAF_ACL_ARN_KEY = "waf-acl-arn"
|
|
33
|
-
|
|
34
31
|
SERVICE_TYPES = [
|
|
35
32
|
"Load Balanced Web Service",
|
|
36
33
|
"Backend Service",
|
|
@@ -42,36 +39,115 @@ class Copilot:
|
|
|
42
39
|
def __init__(
|
|
43
40
|
self,
|
|
44
41
|
config_provider: ConfigProvider,
|
|
42
|
+
parameter_provider: ParameterStore,
|
|
45
43
|
file_provider: FileProvider,
|
|
46
44
|
copilot_templating: CopilotTemplating,
|
|
47
45
|
kms_provider: KMSProvider,
|
|
46
|
+
session,
|
|
47
|
+
io: ClickIOProvider = ClickIOProvider(),
|
|
48
|
+
yaml_file_provider: YamlFileProvider = YamlFileProvider,
|
|
48
49
|
):
|
|
49
50
|
self.config_provider = config_provider
|
|
51
|
+
self.parameter_provider = parameter_provider
|
|
50
52
|
self.file_provider = file_provider
|
|
51
53
|
self.copilot_templating = copilot_templating
|
|
52
54
|
self.kms_provider = kms_provider
|
|
55
|
+
self.io = io
|
|
56
|
+
self.yaml_file_provider = yaml_file_provider
|
|
57
|
+
self.session = session
|
|
58
|
+
|
|
59
|
+
def make_addons(self):
|
|
60
|
+
config = self.config_provider.load_and_validate_platform_config()
|
|
61
|
+
|
|
62
|
+
templates = setup_templates()
|
|
63
|
+
extensions = self._get_extensions()
|
|
64
|
+
application_name = get_application_name()
|
|
65
|
+
|
|
66
|
+
self.io.info("\n>>> Generating Terraform compatible addons CloudFormation\n")
|
|
67
|
+
|
|
68
|
+
output_dir = Path(".").absolute()
|
|
69
|
+
env_path = Path(f"copilot/environments/")
|
|
70
|
+
env_addons_path = env_path / "addons"
|
|
71
|
+
env_overrides_path = env_path / "overrides"
|
|
72
|
+
|
|
73
|
+
self._cleanup_old_files(extensions, output_dir, env_addons_path, env_overrides_path)
|
|
74
|
+
self._generate_env_overrides(output_dir)
|
|
75
|
+
|
|
76
|
+
svc_names = self._list_copilot_local_services()
|
|
77
|
+
base_path = Path(".")
|
|
78
|
+
for svc_name in svc_names:
|
|
79
|
+
self._generate_svc_overrides(base_path, templates, svc_name)
|
|
53
80
|
|
|
54
|
-
|
|
81
|
+
services = []
|
|
82
|
+
for ext_name, ext_data in extensions.items():
|
|
83
|
+
extension = {**ext_data}
|
|
84
|
+
addon_type = extension.pop("type")
|
|
85
|
+
environments = extension.pop("environments")
|
|
86
|
+
environment_addon_config = {
|
|
87
|
+
"addon_type": addon_type,
|
|
88
|
+
"environments": environments,
|
|
89
|
+
"name": extension.get("name", None) or ext_name,
|
|
90
|
+
"prefix": camel_case(ext_name),
|
|
91
|
+
"secret_name": ext_name.upper().replace("-", "_"),
|
|
92
|
+
**extension,
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
services.append(environment_addon_config)
|
|
96
|
+
|
|
97
|
+
service_addon_config = {
|
|
98
|
+
"application_name": application_name,
|
|
99
|
+
"name": extension.get("name", None) or ext_name,
|
|
100
|
+
"prefix": camel_case(ext_name),
|
|
101
|
+
"environments": environments,
|
|
102
|
+
**extension,
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
log_destination_arns = self._get_log_destination_arn()
|
|
106
|
+
|
|
107
|
+
if addon_type in ["s3", "s3-policy"]:
|
|
108
|
+
if extensions[ext_name].get("serve_static_content"):
|
|
109
|
+
continue
|
|
110
|
+
|
|
111
|
+
s3_kms_arns = self._get_s3_kms_alias_arns(application_name, environments)
|
|
112
|
+
for environment_name in environments:
|
|
113
|
+
environments[environment_name]["kms_key_arn"] = s3_kms_arns.get(
|
|
114
|
+
environment_name, "kms-key-not-found"
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
self._generate_service_addons(
|
|
118
|
+
extension,
|
|
119
|
+
ext_name,
|
|
120
|
+
addon_type,
|
|
121
|
+
output_dir,
|
|
122
|
+
service_addon_config,
|
|
123
|
+
templates,
|
|
124
|
+
log_destination_arns,
|
|
125
|
+
)
|
|
126
|
+
|
|
127
|
+
environments = self.config_provider.apply_environment_defaults(config)["environments"]
|
|
128
|
+
|
|
129
|
+
self.copilot_templating.generate_cross_account_s3_policies(environments, extensions)
|
|
130
|
+
|
|
131
|
+
self.io.info(templates.get_template("addon-instructions.txt").render(services=services))
|
|
132
|
+
|
|
133
|
+
def _list_copilot_local_environments(self):
|
|
55
134
|
return [
|
|
56
135
|
path.parent.parts[-1] for path in Path("./copilot/environments/").glob("*/manifest.yml")
|
|
57
136
|
]
|
|
58
137
|
|
|
59
|
-
def
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
click.style(f"No type defined in manifest file {str(path)}; exiting", fg="red")
|
|
65
|
-
)
|
|
66
|
-
exit(1)
|
|
138
|
+
def _is_service(self, path: PosixPath) -> bool:
|
|
139
|
+
|
|
140
|
+
manifest_file = self.yaml_file_provider.load(path)
|
|
141
|
+
if not manifest_file or not manifest_file.get("type"):
|
|
142
|
+
self.io.abort_with_error(f"No type defined in manifest file {str(path)}; exiting")
|
|
67
143
|
|
|
68
|
-
|
|
144
|
+
return manifest_file.get("type") in self.SERVICE_TYPES
|
|
69
145
|
|
|
70
|
-
def
|
|
146
|
+
def _list_copilot_local_services(self):
|
|
71
147
|
return [
|
|
72
148
|
path.parent.parts[-1]
|
|
73
149
|
for path in Path("./copilot/").glob("*/manifest.yml")
|
|
74
|
-
if self.
|
|
150
|
+
if self._is_service(path)
|
|
75
151
|
]
|
|
76
152
|
|
|
77
153
|
def _validate_and_normalise_extensions_config(self, config_file, key_in_config_file=None):
|
|
@@ -93,12 +169,10 @@ class Copilot:
|
|
|
93
169
|
def _normalise_keys(source: dict):
|
|
94
170
|
return {k.replace("-", "_"): v for k, v in source.items()}
|
|
95
171
|
|
|
96
|
-
|
|
97
|
-
addon_plans = yaml.safe_load(fd)
|
|
172
|
+
addon_plans = self.yaml_file_provider.load(self.PACKAGE_DIR / "addon-plans.yml")
|
|
98
173
|
|
|
99
174
|
# load and validate config
|
|
100
|
-
|
|
101
|
-
config = yaml.safe_load(fd)
|
|
175
|
+
config = self.yaml_file_provider.load(config_file)
|
|
102
176
|
|
|
103
177
|
if config and key_in_config_file:
|
|
104
178
|
config = config[key_in_config_file]
|
|
@@ -110,23 +184,19 @@ class Copilot:
|
|
|
110
184
|
errors = validate_addons(config)
|
|
111
185
|
|
|
112
186
|
if errors:
|
|
113
|
-
|
|
187
|
+
self.io.error(f"Errors found in {config_file}:")
|
|
114
188
|
for addon, error in errors.items():
|
|
115
|
-
|
|
116
|
-
|
|
189
|
+
self.io.error(f"Addon '{addon}': {error}")
|
|
190
|
+
self.io.abort_with_error("Invalid platform-config.yml provided, see above warnings")
|
|
117
191
|
|
|
118
|
-
env_names = self.
|
|
119
|
-
svc_names = self.
|
|
192
|
+
env_names = self._list_copilot_local_environments()
|
|
193
|
+
svc_names = self._list_copilot_local_services()
|
|
120
194
|
|
|
121
195
|
if not env_names:
|
|
122
|
-
|
|
123
|
-
click.style(f"No environments found in ./copilot/environments; exiting", fg="red")
|
|
124
|
-
)
|
|
125
|
-
exit(1)
|
|
196
|
+
self.io.abort_with_error("No environments found in ./copilot/environments; exiting")
|
|
126
197
|
|
|
127
198
|
if not svc_names:
|
|
128
|
-
|
|
129
|
-
exit(1)
|
|
199
|
+
self.io.abort_with_error("No services found in ./copilot/; exiting")
|
|
130
200
|
|
|
131
201
|
normalised_config = {}
|
|
132
202
|
config_has_errors = False
|
|
@@ -139,11 +209,8 @@ class Copilot:
|
|
|
139
209
|
normalised_config[addon_name]["services"] = svc_names
|
|
140
210
|
|
|
141
211
|
if not set(normalised_config[addon_name]["services"]).issubset(set(svc_names)):
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
f"Services listed in {addon_name}.services do not exist in ./copilot/",
|
|
145
|
-
fg="red",
|
|
146
|
-
),
|
|
212
|
+
self.io.error(
|
|
213
|
+
f"Services listed in {addon_name}.services do not exist in ./copilot/"
|
|
147
214
|
)
|
|
148
215
|
config_has_errors = True
|
|
149
216
|
|
|
@@ -154,18 +221,10 @@ class Copilot:
|
|
|
154
221
|
|
|
155
222
|
missing_envs = set(environments.keys()) - set(env_names)
|
|
156
223
|
if missing_envs:
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
f"Environment keys listed in {addon_name} do not match those defined in ./copilot/environments.",
|
|
160
|
-
fg="red",
|
|
161
|
-
)
|
|
162
|
-
),
|
|
163
|
-
click.echo(
|
|
164
|
-
click.style(
|
|
165
|
-
f" Missing environments: {', '.join(sorted(missing_envs))}",
|
|
166
|
-
fg="white",
|
|
167
|
-
),
|
|
224
|
+
self.io.error(
|
|
225
|
+
f"Environment keys listed in {addon_name} do not match those defined in ./copilot/environments"
|
|
168
226
|
)
|
|
227
|
+
self.io.error(f" Missing environments: {', '.join(sorted(missing_envs))}")
|
|
169
228
|
config_has_errors = True
|
|
170
229
|
|
|
171
230
|
if config_has_errors:
|
|
@@ -186,41 +245,41 @@ class Copilot:
|
|
|
186
245
|
normalised_config[addon_name]["environments"] = normalised_environments
|
|
187
246
|
|
|
188
247
|
if config_has_errors:
|
|
189
|
-
|
|
248
|
+
self.io.abort_with_error("Configuration has errors. Exiting.")
|
|
190
249
|
|
|
191
250
|
return normalised_config
|
|
192
251
|
|
|
193
|
-
def
|
|
252
|
+
def _get_log_destination_arn(self):
|
|
194
253
|
"""Get destination arns stored in param store in projects aws
|
|
195
254
|
account."""
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
fg="red",
|
|
205
|
-
)
|
|
255
|
+
|
|
256
|
+
try:
|
|
257
|
+
destination_arns = self.parameter_provider.get_ssm_parameter_by_name(
|
|
258
|
+
"/copilot/tools/central_log_groups"
|
|
259
|
+
)
|
|
260
|
+
except botocore.errorfactory.ParameterNotFound:
|
|
261
|
+
self.io.abort_with_error(
|
|
262
|
+
"No aws central log group defined in Parameter Store at location /copilot/tools/central_log_groups; exiting"
|
|
206
263
|
)
|
|
207
|
-
exit(1)
|
|
208
264
|
|
|
209
|
-
|
|
210
|
-
return destination_arns
|
|
265
|
+
return json.loads(destination_arns["Value"])
|
|
211
266
|
|
|
212
267
|
def _generate_svc_overrides(self, base_path, templates, name):
|
|
213
|
-
|
|
268
|
+
self.io.info(f"\n>>> Generating service overrides for {name}\n")
|
|
214
269
|
overrides_path = base_path.joinpath(f"copilot/{name}/overrides")
|
|
215
270
|
overrides_path.mkdir(parents=True, exist_ok=True)
|
|
216
271
|
overrides_file = overrides_path.joinpath("cfn.patches.yml")
|
|
217
272
|
overrides_file.write_text(templates.get_template("svc/overrides/cfn.patches.yml").render())
|
|
218
273
|
|
|
219
|
-
def _get_s3_kms_alias_arns(self,
|
|
220
|
-
application = load_application(application_name, session)
|
|
274
|
+
def _get_s3_kms_alias_arns(self, application_name, config):
|
|
275
|
+
application = load_application(application_name, self.session)
|
|
221
276
|
arns = {}
|
|
222
277
|
|
|
223
278
|
for environment_name in application.environments:
|
|
279
|
+
kms_provider = self.kms_provider(
|
|
280
|
+
application.environments[environment_name].session.client("kms")
|
|
281
|
+
)
|
|
282
|
+
|
|
224
283
|
if environment_name not in config:
|
|
225
284
|
continue
|
|
226
285
|
|
|
@@ -228,99 +287,20 @@ class Copilot:
|
|
|
228
287
|
continue
|
|
229
288
|
|
|
230
289
|
bucket_name = config[environment_name]["bucket_name"]
|
|
231
|
-
kms_client = application.environments[environment_name].session.client("kms")
|
|
232
290
|
alias_name = f"alias/{application_name}-{environment_name}-{bucket_name}-key"
|
|
233
291
|
|
|
234
292
|
try:
|
|
235
|
-
response =
|
|
236
|
-
|
|
237
|
-
|
|
293
|
+
response = kms_provider.describe_key(alias_name)
|
|
294
|
+
|
|
295
|
+
# Boto3 classifies all AWS service errors and exceptions as ClientError exceptions
|
|
296
|
+
except botocore.exceptions.ClientError as error:
|
|
297
|
+
if error.response["Error"]["Code"] == "NotFoundException":
|
|
298
|
+
pass
|
|
238
299
|
else:
|
|
239
300
|
arns[environment_name] = response["KeyMetadata"]["Arn"]
|
|
240
301
|
|
|
241
302
|
return arns
|
|
242
303
|
|
|
243
|
-
def make_addons(self):
|
|
244
|
-
self.config_provider.config_file_check()
|
|
245
|
-
try:
|
|
246
|
-
config = self.config_provider.load_and_validate_platform_config()
|
|
247
|
-
except SchemaError as ex:
|
|
248
|
-
click.secho(f"Invalid `{PLATFORM_CONFIG_FILE}` file: {str(ex)}", fg="red")
|
|
249
|
-
raise click.Abort
|
|
250
|
-
|
|
251
|
-
templates = setup_templates()
|
|
252
|
-
extensions = self._get_extensions()
|
|
253
|
-
session = get_aws_session_or_abort()
|
|
254
|
-
|
|
255
|
-
application_name = get_application_name()
|
|
256
|
-
|
|
257
|
-
click.echo("\n>>> Generating Terraform compatible addons CloudFormation\n")
|
|
258
|
-
|
|
259
|
-
output_dir = Path(".").absolute()
|
|
260
|
-
env_path = Path(f"copilot/environments/")
|
|
261
|
-
env_addons_path = env_path / "addons"
|
|
262
|
-
env_overrides_path = env_path / "overrides"
|
|
263
|
-
|
|
264
|
-
self._cleanup_old_files(extensions, output_dir, env_addons_path, env_overrides_path)
|
|
265
|
-
self._generate_env_overrides(output_dir)
|
|
266
|
-
|
|
267
|
-
svc_names = self.list_copilot_local_services()
|
|
268
|
-
base_path = Path(".")
|
|
269
|
-
for svc_name in svc_names:
|
|
270
|
-
self._generate_svc_overrides(base_path, templates, svc_name)
|
|
271
|
-
|
|
272
|
-
services = []
|
|
273
|
-
for ext_name, ext_data in extensions.items():
|
|
274
|
-
extension = {**ext_data}
|
|
275
|
-
addon_type = extension.pop("type")
|
|
276
|
-
environments = extension.pop("environments")
|
|
277
|
-
environment_addon_config = {
|
|
278
|
-
"addon_type": addon_type,
|
|
279
|
-
"environments": environments,
|
|
280
|
-
"name": extension.get("name", None) or ext_name,
|
|
281
|
-
"prefix": camel_case(ext_name),
|
|
282
|
-
"secret_name": ext_name.upper().replace("-", "_"),
|
|
283
|
-
**extension,
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
services.append(environment_addon_config)
|
|
287
|
-
|
|
288
|
-
service_addon_config = {
|
|
289
|
-
"application_name": application_name,
|
|
290
|
-
"name": extension.get("name", None) or ext_name,
|
|
291
|
-
"prefix": camel_case(ext_name),
|
|
292
|
-
"environments": environments,
|
|
293
|
-
**extension,
|
|
294
|
-
}
|
|
295
|
-
|
|
296
|
-
log_destination_arns = self.get_log_destination_arn()
|
|
297
|
-
|
|
298
|
-
if addon_type in ["s3", "s3-policy"]:
|
|
299
|
-
if extensions[ext_name].get("serve_static_content"):
|
|
300
|
-
continue
|
|
301
|
-
|
|
302
|
-
s3_kms_arns = self._get_s3_kms_alias_arns(session, application_name, environments)
|
|
303
|
-
for environment_name in environments:
|
|
304
|
-
environments[environment_name]["kms_key_arn"] = s3_kms_arns.get(
|
|
305
|
-
environment_name, "kms-key-not-found"
|
|
306
|
-
)
|
|
307
|
-
|
|
308
|
-
self._generate_service_addons(
|
|
309
|
-
extension,
|
|
310
|
-
ext_name,
|
|
311
|
-
addon_type,
|
|
312
|
-
output_dir,
|
|
313
|
-
service_addon_config,
|
|
314
|
-
templates,
|
|
315
|
-
log_destination_arns,
|
|
316
|
-
)
|
|
317
|
-
|
|
318
|
-
environments = self.config_provider.apply_environment_defaults(config)["environments"]
|
|
319
|
-
|
|
320
|
-
self.copilot_templating.generate_cross_account_s3_policies(environments, extensions)
|
|
321
|
-
|
|
322
|
-
click.echo(templates.get_template("addon-instructions.txt").render(services=services))
|
|
323
|
-
|
|
324
304
|
def _get_extensions(self):
|
|
325
305
|
config = self._validate_and_normalise_extensions_config(
|
|
326
306
|
self.PACKAGE_DIR / "default-extensions.yml"
|
|
@@ -331,13 +311,31 @@ class Copilot:
|
|
|
331
311
|
config.update(project_config)
|
|
332
312
|
return config
|
|
333
313
|
|
|
314
|
+
def _generate_override_files(self, base_path, file_path, output_dir):
|
|
315
|
+
def generate_files_for_dir(pattern):
|
|
316
|
+
for file in file_path.glob(pattern):
|
|
317
|
+
if file.is_file():
|
|
318
|
+
contents = file.read_text()
|
|
319
|
+
file_name = str(file).removeprefix(f"{file_path}/")
|
|
320
|
+
self.io.info(
|
|
321
|
+
self.file_provider.mkfile(
|
|
322
|
+
base_path,
|
|
323
|
+
output_dir / file_name,
|
|
324
|
+
contents,
|
|
325
|
+
overwrite=True,
|
|
326
|
+
)
|
|
327
|
+
)
|
|
328
|
+
|
|
329
|
+
generate_files_for_dir("*")
|
|
330
|
+
generate_files_for_dir("bin/*")
|
|
331
|
+
|
|
334
332
|
def _generate_env_overrides(self, output_dir):
|
|
335
333
|
path = "templates/env/terraform-overrides"
|
|
336
|
-
|
|
334
|
+
self.io.info("\n>>> Generating Environment overrides\n")
|
|
337
335
|
overrides_path = output_dir.joinpath(f"copilot/environments/overrides")
|
|
338
336
|
overrides_path.mkdir(parents=True, exist_ok=True)
|
|
339
337
|
template_overrides_path = Path(__file__).parent.parent.joinpath(path)
|
|
340
|
-
|
|
338
|
+
self._generate_override_files(Path("."), template_overrides_path, overrides_path)
|
|
341
339
|
|
|
342
340
|
def _generate_service_addons(
|
|
343
341
|
self,
|
|
@@ -364,7 +362,7 @@ class Copilot:
|
|
|
364
362
|
)
|
|
365
363
|
|
|
366
364
|
(output_dir / service_path).mkdir(parents=True, exist_ok=True)
|
|
367
|
-
|
|
365
|
+
self.io.info(
|
|
368
366
|
self.file_provider.mkfile(
|
|
369
367
|
output_dir, service_path / f"{addon_name}.yml", contents, overwrite=True
|
|
370
368
|
)
|
|
@@ -9,11 +9,16 @@ from dbt_platform_helper.providers.semantic_version import (
|
|
|
9
9
|
from dbt_platform_helper.providers.semantic_version import (
|
|
10
10
|
IncompatibleMinorVersionException,
|
|
11
11
|
)
|
|
12
|
-
from dbt_platform_helper.providers.semantic_version import PlatformHelperVersionStatus
|
|
13
12
|
from dbt_platform_helper.providers.semantic_version import SemanticVersion
|
|
13
|
+
from dbt_platform_helper.providers.version import AWSCLIInstalledVersionProvider
|
|
14
|
+
from dbt_platform_helper.providers.version import CopilotInstalledVersionProvider
|
|
14
15
|
from dbt_platform_helper.providers.version import DeprecatedVersionFileVersionProvider
|
|
16
|
+
from dbt_platform_helper.providers.version import GithubLatestVersionProvider
|
|
15
17
|
from dbt_platform_helper.providers.version import InstalledVersionProvider
|
|
16
|
-
from dbt_platform_helper.providers.version import
|
|
18
|
+
from dbt_platform_helper.providers.version import PyPiLatestVersionProvider
|
|
19
|
+
from dbt_platform_helper.providers.version import VersionProvider
|
|
20
|
+
from dbt_platform_helper.providers.version_status import PlatformHelperVersionStatus
|
|
21
|
+
from dbt_platform_helper.providers.version_status import VersionStatus
|
|
17
22
|
from dbt_platform_helper.providers.yaml_file import YamlFileProvider
|
|
18
23
|
|
|
19
24
|
|
|
@@ -38,14 +43,14 @@ class PlatformHelperVersioning:
|
|
|
38
43
|
YamlFileProvider
|
|
39
44
|
),
|
|
40
45
|
config_provider: ConfigProvider = ConfigProvider(),
|
|
41
|
-
|
|
46
|
+
latest_version_provider: VersionProvider = PyPiLatestVersionProvider,
|
|
42
47
|
installed_version_provider: InstalledVersionProvider = InstalledVersionProvider(),
|
|
43
48
|
skip_versioning_checks: bool = None,
|
|
44
49
|
):
|
|
45
50
|
self.io = io
|
|
46
51
|
self.version_file_version_provider = version_file_version_provider
|
|
47
52
|
self.config_provider = config_provider
|
|
48
|
-
self.
|
|
53
|
+
self.latest_version_provider = latest_version_provider
|
|
49
54
|
self.installed_version_provider = installed_version_provider
|
|
50
55
|
self.skip_versioning_checks = (
|
|
51
56
|
skip_versioning_checks if skip_versioning_checks is not None else skip_version_checks()
|
|
@@ -100,11 +105,11 @@ class PlatformHelperVersioning:
|
|
|
100
105
|
self,
|
|
101
106
|
include_project_versions: bool = True,
|
|
102
107
|
) -> PlatformHelperVersionStatus:
|
|
103
|
-
locally_installed_version = self.installed_version_provider.
|
|
108
|
+
locally_installed_version = self.installed_version_provider.get_semantic_version(
|
|
104
109
|
"dbt-platform-helper"
|
|
105
110
|
)
|
|
106
111
|
|
|
107
|
-
latest_release = self.
|
|
112
|
+
latest_release = self.latest_version_provider.get_semantic_version("dbt-platform-helper")
|
|
108
113
|
|
|
109
114
|
if not include_project_versions:
|
|
110
115
|
return PlatformHelperVersionStatus(
|
|
@@ -129,7 +134,7 @@ class PlatformHelperVersioning:
|
|
|
129
134
|
out = PlatformHelperVersionStatus(
|
|
130
135
|
installed=locally_installed_version,
|
|
131
136
|
latest=latest_release,
|
|
132
|
-
deprecated_version_file=self.version_file_version_provider.
|
|
137
|
+
deprecated_version_file=self.version_file_version_provider.get_semantic_version(),
|
|
133
138
|
platform_config_default=platform_config_default,
|
|
134
139
|
pipeline_overrides=pipeline_overrides,
|
|
135
140
|
)
|
|
@@ -155,3 +160,39 @@ class PlatformHelperVersioning:
|
|
|
155
160
|
raise PlatformHelperVersionNotFoundException
|
|
156
161
|
|
|
157
162
|
return out
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
class AWSVersioning:
|
|
166
|
+
def __init__(
|
|
167
|
+
self,
|
|
168
|
+
latest_version_provider: VersionProvider = None,
|
|
169
|
+
installed_version_provider: VersionProvider = None,
|
|
170
|
+
):
|
|
171
|
+
self.latest_version_provider = latest_version_provider or GithubLatestVersionProvider
|
|
172
|
+
self.installed_version_provider = (
|
|
173
|
+
installed_version_provider or AWSCLIInstalledVersionProvider
|
|
174
|
+
)
|
|
175
|
+
|
|
176
|
+
def get_version_status(self) -> VersionStatus:
|
|
177
|
+
return VersionStatus(
|
|
178
|
+
self.installed_version_provider.get_semantic_version(),
|
|
179
|
+
self.latest_version_provider.get_semantic_version("aws/aws-cli", True),
|
|
180
|
+
)
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
class CopilotVersioning:
|
|
184
|
+
def __init__(
|
|
185
|
+
self,
|
|
186
|
+
latest_version_provider: VersionProvider = None,
|
|
187
|
+
installed_version_provider: VersionProvider = None,
|
|
188
|
+
):
|
|
189
|
+
self.latest_version_provider = latest_version_provider or GithubLatestVersionProvider
|
|
190
|
+
self.installed_version_provider = (
|
|
191
|
+
installed_version_provider or CopilotInstalledVersionProvider
|
|
192
|
+
)
|
|
193
|
+
|
|
194
|
+
def get_version_status(self) -> VersionStatus:
|
|
195
|
+
return VersionStatus(
|
|
196
|
+
self.installed_version_provider.get_semantic_version(),
|
|
197
|
+
self.latest_version_provider.get_semantic_version("aws/copilot-cli"),
|
|
198
|
+
)
|
|
File without changes
|
|
@@ -35,3 +35,13 @@ class CopilotCodebaseNotFoundException(PlatformException):
|
|
|
35
35
|
super().__init__(
|
|
36
36
|
f"""The codebase "{codebase}" either does not exist or has not been deployed."""
|
|
37
37
|
)
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class CreateAccessTokenException(AWSException):
|
|
41
|
+
def __init__(self, client_id: str):
|
|
42
|
+
super().__init__(f"""Failed to create access token for Client "{client_id}".""")
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
class UnableToRetrieveSSOAccountList(AWSException):
|
|
46
|
+
def __init__(self):
|
|
47
|
+
super().__init__("Unable to retrieve AWS SSO account list")
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import botocore
|
|
2
|
+
from boto3 import Session
|
|
3
|
+
|
|
4
|
+
from dbt_platform_helper.providers.aws.exceptions import CreateAccessTokenException
|
|
5
|
+
from dbt_platform_helper.providers.aws.exceptions import UnableToRetrieveSSOAccountList
|
|
6
|
+
from dbt_platform_helper.utils.aws import get_aws_session_or_abort
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class SSOAuthProvider:
|
|
10
|
+
def __init__(self, session: Session = None):
|
|
11
|
+
self.session = session
|
|
12
|
+
self.sso_oidc = self._get_client("sso-oidc")
|
|
13
|
+
self.sso = self._get_client("sso")
|
|
14
|
+
|
|
15
|
+
def register(self, client_name, client_type):
|
|
16
|
+
client = self.sso_oidc.register_client(clientName=client_name, clientType=client_type)
|
|
17
|
+
client_id = client.get("clientId")
|
|
18
|
+
client_secret = client.get("clientSecret")
|
|
19
|
+
|
|
20
|
+
return client_id, client_secret
|
|
21
|
+
|
|
22
|
+
def start_device_authorization(self, client_id, client_secret, start_url):
|
|
23
|
+
authz = self.sso_oidc.start_device_authorization(
|
|
24
|
+
clientId=client_id,
|
|
25
|
+
clientSecret=client_secret,
|
|
26
|
+
startUrl=start_url,
|
|
27
|
+
)
|
|
28
|
+
url = authz.get("verificationUriComplete")
|
|
29
|
+
deviceCode = authz.get("deviceCode")
|
|
30
|
+
|
|
31
|
+
return url, deviceCode
|
|
32
|
+
|
|
33
|
+
def create_access_token(self, client_id, client_secret, device_code):
|
|
34
|
+
try:
|
|
35
|
+
response = self.sso_oidc.create_token(
|
|
36
|
+
clientId=client_id,
|
|
37
|
+
clientSecret=client_secret,
|
|
38
|
+
grantType="urn:ietf:params:oauth:grant-type:device_code",
|
|
39
|
+
deviceCode=device_code,
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
return response.get("accessToken")
|
|
43
|
+
|
|
44
|
+
except botocore.exceptions.ClientError as e:
|
|
45
|
+
if e.response["Error"]["Code"] != "AuthorizationPendingException":
|
|
46
|
+
raise CreateAccessTokenException(client_id)
|
|
47
|
+
|
|
48
|
+
def list_accounts(self, access_token, max_results=100):
|
|
49
|
+
aws_accounts_response = self.sso.list_accounts(
|
|
50
|
+
accessToken=access_token,
|
|
51
|
+
maxResults=max_results,
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
if len(aws_accounts_response.get("accountList", [])) == 0:
|
|
55
|
+
raise UnableToRetrieveSSOAccountList()
|
|
56
|
+
return aws_accounts_response.get("accountList")
|
|
57
|
+
|
|
58
|
+
def _get_client(self, client: str):
|
|
59
|
+
if not self.session:
|
|
60
|
+
self.session = get_aws_session_or_abort()
|
|
61
|
+
return self.session.client(client)
|
|
@@ -64,7 +64,8 @@ class ConfigProvider:
|
|
|
64
64
|
except FileProviderException:
|
|
65
65
|
return {}
|
|
66
66
|
|
|
67
|
-
# TODO
|
|
67
|
+
# TODO remove function and push logic to where this is called.
|
|
68
|
+
# removed usage from config domain, code is very generic and doesn't require the overhead of a function
|
|
68
69
|
def config_file_check(self, path=PLATFORM_CONFIG_FILE):
|
|
69
70
|
if not Path(path).exists():
|
|
70
71
|
self.io.abort_with_error(
|