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.

Files changed (35) hide show
  1. dbt_platform_helper/commands/codebase.py +10 -4
  2. dbt_platform_helper/commands/config.py +12 -314
  3. dbt_platform_helper/commands/copilot.py +10 -6
  4. dbt_platform_helper/commands/database.py +17 -9
  5. dbt_platform_helper/commands/environment.py +2 -3
  6. dbt_platform_helper/domain/codebase.py +7 -7
  7. dbt_platform_helper/domain/config.py +345 -0
  8. dbt_platform_helper/domain/copilot.py +155 -157
  9. dbt_platform_helper/domain/versioning.py +48 -7
  10. dbt_platform_helper/providers/aws/__init__.py +0 -0
  11. dbt_platform_helper/providers/aws/exceptions.py +10 -0
  12. dbt_platform_helper/providers/aws/sso_auth.py +61 -0
  13. dbt_platform_helper/providers/config.py +2 -1
  14. dbt_platform_helper/providers/config_validator.py +15 -13
  15. dbt_platform_helper/providers/io.py +2 -2
  16. dbt_platform_helper/providers/parameter_store.py +47 -0
  17. dbt_platform_helper/providers/platform_config_schema.py +17 -0
  18. dbt_platform_helper/providers/semantic_version.py +15 -88
  19. dbt_platform_helper/providers/terraform_manifest.py +1 -0
  20. dbt_platform_helper/providers/version.py +82 -24
  21. dbt_platform_helper/providers/version_status.py +80 -0
  22. dbt_platform_helper/utils/aws.py +0 -141
  23. dbt_platform_helper/utils/git.py +3 -1
  24. dbt_platform_helper/utils/tool_versioning.py +0 -84
  25. {dbt_platform_helper-13.2.0.dist-info → dbt_platform_helper-13.3.0.dist-info}/METADATA +2 -2
  26. {dbt_platform_helper-13.2.0.dist-info → dbt_platform_helper-13.3.0.dist-info}/RECORD +30 -30
  27. {dbt_platform_helper-13.2.0.dist-info → dbt_platform_helper-13.3.0.dist-info}/WHEEL +1 -1
  28. platform_helper.py +1 -1
  29. dbt_platform_helper/templates/svc/manifest-backend.yml +0 -69
  30. dbt_platform_helper/templates/svc/manifest-public.yml +0 -109
  31. dbt_platform_helper/utils/cloudfoundry.py +0 -14
  32. dbt_platform_helper/utils/files.py +0 -53
  33. dbt_platform_helper/utils/manifests.py +0 -18
  34. {dbt_platform_helper-13.2.0.dist-info → dbt_platform_helper-13.3.0.dist-info}/LICENSE +0 -0
  35. {dbt_platform_helper-13.2.0.dist-info → dbt_platform_helper-13.3.0.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,345 @@
1
+ import os
2
+ import re
3
+ import webbrowser
4
+ from pathlib import Path
5
+ from typing import Dict
6
+
7
+ from prettytable import PrettyTable
8
+
9
+ from dbt_platform_helper.constants import PLATFORM_CONFIG_FILE
10
+ from dbt_platform_helper.domain.versioning import AWSVersioning
11
+ from dbt_platform_helper.domain.versioning import CopilotVersioning
12
+ from dbt_platform_helper.domain.versioning import PlatformHelperVersioning
13
+ from dbt_platform_helper.platform_exception import PlatformException
14
+ from dbt_platform_helper.providers.aws.sso_auth import SSOAuthProvider
15
+ from dbt_platform_helper.providers.io import ClickIOProvider
16
+ from dbt_platform_helper.providers.semantic_version import (
17
+ IncompatibleMajorVersionException,
18
+ )
19
+ from dbt_platform_helper.providers.semantic_version import SemanticVersion
20
+ from dbt_platform_helper.providers.validation import ValidationException
21
+ from dbt_platform_helper.providers.version_status import PlatformHelperVersionStatus
22
+ from dbt_platform_helper.providers.version_status import VersionStatus
23
+
24
+ yes = "\033[92m✔\033[0m"
25
+ no = "\033[91m✖\033[0m"
26
+ maybe = "\033[93m?\033[0m"
27
+
28
+ RECOMMENDATIONS = {
29
+ "dbt-platform-helper-upgrade": (
30
+ "Upgrade dbt-platform-helper to version {version} `pip install "
31
+ "--upgrade dbt-platform-helper=={version}`."
32
+ ),
33
+ "dbt-platform-helper-upgrade-note": (
34
+ "Post upgrade, run `platform-helper copilot make-addons` to " "update your addon templates."
35
+ ),
36
+ "generic-tool-upgrade": "Upgrade {tool} to version {version}.",
37
+ "install-copilot": "Install AWS Copilot https://aws.github.io/copilot-cli/",
38
+ "install-aws": "Install AWS CLI https://aws.amazon.com/cli/",
39
+ }
40
+
41
+ AWS_CONFIG = """
42
+ #
43
+ # uktrade
44
+ #
45
+
46
+ [sso-session uktrade]
47
+ sso_start_url = https://uktrade.awsapps.com/start#/
48
+ sso_region = eu-west-2
49
+ sso_registration_scopes = sso:account:access
50
+
51
+ [default]
52
+ sso_session = uktrade
53
+ region = eu-west-2
54
+ output = json
55
+
56
+ """
57
+
58
+
59
+ class NoDeploymentRepoConfigException(PlatformException):
60
+ def __init__(self):
61
+ super().__init__("Could not find a deployment repository, no checks to run.")
62
+
63
+
64
+ # TODO move to generic location so it can be reused
65
+ class NoPlatformConfigException(PlatformException):
66
+ def __init__(self):
67
+ super().__init__(
68
+ f"`platform-config.yml` is missing. "
69
+ "Please check it exists and you are in the root directory of your deployment project."
70
+ )
71
+
72
+
73
+ class Config:
74
+
75
+ def __init__(
76
+ self,
77
+ io: ClickIOProvider = ClickIOProvider(),
78
+ platform_helper_versioning: PlatformHelperVersioning = PlatformHelperVersioning(),
79
+ aws_versioning: AWSVersioning = AWSVersioning(),
80
+ copilot_versioning: CopilotVersioning = CopilotVersioning(),
81
+ sso: SSOAuthProvider = None,
82
+ ):
83
+ self.oidc_app = None
84
+ self.io = io
85
+ self.platform_helper_versioning = platform_helper_versioning
86
+ self.aws_versioning = aws_versioning
87
+ self.copilot_versioning = copilot_versioning
88
+ self.sso = sso or SSOAuthProvider()
89
+ self.SSO_START_URL = "https://uktrade.awsapps.com/start"
90
+
91
+ def validate(self):
92
+ if not Path("copilot").exists():
93
+ raise NoDeploymentRepoConfigException()
94
+ if not Path(PLATFORM_CONFIG_FILE).exists():
95
+ raise NoPlatformConfigException()
96
+
97
+ self.io.debug("\nDetected a deployment repository\n")
98
+ platform_helper_version_status = self.platform_helper_versioning._get_version_status(
99
+ include_project_versions=True
100
+ )
101
+ self.io.process_messages(platform_helper_version_status.validate())
102
+ aws_version_status = self.aws_versioning.get_version_status()
103
+ copilot_version_status = self.copilot_versioning.get_version_status()
104
+
105
+ self._check_tool_versions(
106
+ platform_helper_version_status, aws_version_status, copilot_version_status
107
+ )
108
+
109
+ compatible = self._check_addon_versions(platform_helper_version_status)
110
+
111
+ exit(0 if compatible else 1)
112
+
113
+ def generate_aws(self, file_path):
114
+ self.oidc_app = self._create_oidc_application()
115
+ verification_url, device_code = self._get_device_code(self.oidc_app)
116
+
117
+ if self.io.confirm(
118
+ "You are about to be redirected to a verification page. You will need to complete sign-in before returning to the command line. Do you want to continue?",
119
+ ):
120
+ webbrowser.open(verification_url)
121
+
122
+ if self.io.confirm(
123
+ "Have you completed the sign-in process in your browser?",
124
+ ):
125
+ access_token = self.sso.create_access_token(
126
+ client_id=self.oidc_app[0],
127
+ client_secret=self.oidc_app[1],
128
+ device_code=device_code,
129
+ )
130
+
131
+ aws_config_path = os.path.expanduser(file_path)
132
+
133
+ if self.io.confirm(
134
+ f"This command is destructive and will overwrite file contents at {file_path}. Are you sure you want to continue?"
135
+ ):
136
+ with open(aws_config_path, "w") as config_file:
137
+ config_file.write(AWS_CONFIG)
138
+
139
+ for account in self._retrieve_aws_accounts(access_token):
140
+ config_file.write(f"[profile {account['accountName']}]\n")
141
+ config_file.write("sso_session = uktrade\n")
142
+ config_file.write(f"sso_account_id = {account['accountId']}\n")
143
+ config_file.write("sso_role_name = AdministratorAccess\n")
144
+ config_file.write("region = eu-west-2\n")
145
+ config_file.write("output = json\n")
146
+ config_file.write("\n")
147
+
148
+ def _create_oidc_application(self):
149
+ self.io.debug("Creating temporary AWS SSO OIDC application")
150
+ client_id, client_secret = self.sso.register(
151
+ client_name="platform-helper",
152
+ client_type="public",
153
+ )
154
+ return client_id, client_secret
155
+
156
+ def _get_device_code(self, oidc_application):
157
+ self.io.debug("Initiating device code flow")
158
+ url, device_code = self.sso.start_device_authorization(
159
+ client_id=oidc_application[0],
160
+ client_secret=oidc_application[1],
161
+ start_url=self.SSO_START_URL,
162
+ )
163
+
164
+ return url, device_code
165
+
166
+ def _retrieve_aws_accounts(self, aws_sso_token):
167
+ accounts_list = self.sso.list_accounts(
168
+ access_token=aws_sso_token,
169
+ max_results=100,
170
+ )
171
+ return accounts_list
172
+
173
+ def _add_version_status_row(
174
+ self, table: PrettyTable, header: str, version_status: VersionStatus
175
+ ):
176
+ table.add_row(
177
+ [
178
+ header,
179
+ str(version_status.installed),
180
+ str(version_status.latest),
181
+ no if version_status.is_outdated() else yes,
182
+ ]
183
+ )
184
+
185
+ def _check_tool_versions(
186
+ self,
187
+ platform_helper_version_status: PlatformHelperVersionStatus,
188
+ aws_version_status: VersionStatus,
189
+ copilot_version_status: VersionStatus,
190
+ ):
191
+ self.io.debug("Checking tooling versions...")
192
+
193
+ recommendations = {}
194
+
195
+ if copilot_version_status.installed is None:
196
+ recommendations["install-copilot"] = RECOMMENDATIONS["install-copilot"]
197
+
198
+ if aws_version_status.installed is None:
199
+ recommendations["install-aws"] = RECOMMENDATIONS["install-aws"]
200
+
201
+ tool_versions_table = PrettyTable()
202
+ tool_versions_table.field_names = [
203
+ "Tool",
204
+ "Local version",
205
+ "Released version",
206
+ "Running latest?",
207
+ ]
208
+ tool_versions_table.align["Tool"] = "l"
209
+
210
+ self._add_version_status_row(tool_versions_table, "aws", aws_version_status)
211
+ self._add_version_status_row(tool_versions_table, "copilot", copilot_version_status)
212
+ self._add_version_status_row(
213
+ tool_versions_table, "dbt-platform-helper", platform_helper_version_status
214
+ )
215
+
216
+ self.io.info(tool_versions_table)
217
+
218
+ if aws_version_status.is_outdated() and "install-aws" not in recommendations:
219
+ recommendations["aws-upgrade"] = RECOMMENDATIONS["generic-tool-upgrade"].format(
220
+ tool="AWS CLI",
221
+ version=str(aws_version_status.latest),
222
+ )
223
+
224
+ if copilot_version_status.is_outdated() and "install-copilot" not in recommendations:
225
+ recommendations["copilot-upgrade"] = RECOMMENDATIONS["generic-tool-upgrade"].format(
226
+ tool="AWS Copilot",
227
+ version=str(copilot_version_status.latest),
228
+ )
229
+
230
+ if platform_helper_version_status.is_outdated():
231
+ recommendations["dbt-platform-helper-upgrade"] = RECOMMENDATIONS[
232
+ "dbt-platform-helper-upgrade"
233
+ ].format(version=str(platform_helper_version_status.latest))
234
+ recommendations["dbt-platform-helper-upgrade-note"] = RECOMMENDATIONS[
235
+ "dbt-platform-helper-upgrade-note"
236
+ ]
237
+
238
+ self._render_recommendations(recommendations)
239
+
240
+ def _check_addon_versions(self, platform_helper_versions: PlatformHelperVersionStatus) -> bool:
241
+
242
+ self.io.debug("Checking addons templates versions...")
243
+
244
+ compatible = True
245
+ recommendations = {}
246
+
247
+ local_version = platform_helper_versions.installed
248
+ latest_release = platform_helper_versions.latest
249
+
250
+ addons_templates_table = PrettyTable()
251
+ addons_templates_table.field_names = [
252
+ "Addons Template File",
253
+ "Generated with",
254
+ "Compatible with local?",
255
+ "Compatible with latest?",
256
+ ]
257
+ addons_templates_table.align["Addons Template File"] = "l"
258
+
259
+ addons_templates = list(Path("./copilot").glob("**/addons/*"))
260
+ # Sort by template file path
261
+ addons_templates.sort(key=lambda e: str(e))
262
+ # Bring environment addons to the top
263
+ addons_templates.sort(key=lambda e: "environments/" not in str(e))
264
+
265
+ for template_file in addons_templates:
266
+ generated_with_version = maybe
267
+ local_compatible_symbol = yes
268
+ latest_compatible_symbol = yes
269
+
270
+ generated_with_version = None
271
+
272
+ try:
273
+ generated_with_version = self.__get_template_generated_with_version(
274
+ str(template_file.resolve())
275
+ )
276
+ except ValidationException:
277
+ local_compatible_symbol = maybe
278
+ compatible = False
279
+ recommendations["dbt-platform-helper-upgrade"] = RECOMMENDATIONS[
280
+ "dbt-platform-helper-upgrade"
281
+ ].format(version=latest_release)
282
+ recommendations["dbt-platform-helper-upgrade-note"] = RECOMMENDATIONS[
283
+ "dbt-platform-helper-upgrade-note"
284
+ ]
285
+
286
+ try:
287
+ local_version.validate_compatibility_with(generated_with_version)
288
+ except IncompatibleMajorVersionException:
289
+ local_compatible_symbol = no
290
+ compatible = False
291
+ recommendations["dbt-platform-helper-upgrade"] = RECOMMENDATIONS[
292
+ "dbt-platform-helper-upgrade"
293
+ ].format(version=latest_release)
294
+ recommendations["dbt-platform-helper-upgrade-note"] = RECOMMENDATIONS[
295
+ "dbt-platform-helper-upgrade-note"
296
+ ]
297
+ except ValidationException:
298
+ local_compatible_symbol = maybe
299
+ compatible = False
300
+
301
+ try:
302
+ latest_release.validate_compatibility_with(generated_with_version)
303
+ except IncompatibleMajorVersionException:
304
+ latest_compatible_symbol = no
305
+ compatible = False
306
+ except ValidationException:
307
+ latest_compatible_symbol = maybe
308
+ compatible = False
309
+
310
+ addons_templates_table.add_row(
311
+ [
312
+ template_file.relative_to("."),
313
+ (maybe if latest_compatible_symbol is maybe else str(generated_with_version)),
314
+ local_compatible_symbol,
315
+ latest_compatible_symbol,
316
+ ]
317
+ )
318
+
319
+ self.io.info(addons_templates_table)
320
+ self._render_recommendations(recommendations)
321
+
322
+ return compatible
323
+
324
+ def _render_recommendations(self, recommendations: Dict[str, str]):
325
+ if recommendations:
326
+ self.io.info("\nRecommendations:\n", bold=True)
327
+
328
+ for name, recommendation in recommendations.items():
329
+ if name.endswith("-note"):
330
+ continue
331
+ self.io.info(f" - {recommendation}")
332
+ if recommendations.get(f"{name}-note", False):
333
+ self.io.info(f" {recommendations.get(f'{name}-note')}")
334
+
335
+ self.io.info("")
336
+
337
+ def __get_template_generated_with_version(self, template_file_path: str) -> SemanticVersion:
338
+ try:
339
+ template_contents = Path(template_file_path).read_text()
340
+ template_version = re.search(
341
+ r"# Generated by platform-helper ([v.\-0-9]+)", template_contents
342
+ ).group(1)
343
+ return SemanticVersion.from_string(template_version)
344
+ except (IndexError, AttributeError):
345
+ raise ValidationException(f"Template {template_file_path} has no version information")