dbt-platform-helper 13.0.0__py3-none-any.whl → 13.0.2__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.md +2 -2
- dbt_platform_helper/commands/config.py +26 -33
- dbt_platform_helper/commands/generate.py +2 -2
- dbt_platform_helper/commands/version.py +30 -30
- dbt_platform_helper/domain/config_validator.py +10 -5
- dbt_platform_helper/domain/copilot_environment.py +10 -9
- dbt_platform_helper/domain/database_copy.py +6 -3
- dbt_platform_helper/domain/maintenance_page.py +32 -7
- dbt_platform_helper/domain/terraform_environment.py +17 -61
- dbt_platform_helper/providers/config.py +11 -1
- dbt_platform_helper/providers/files.py +13 -12
- dbt_platform_helper/providers/load_balancers.py +4 -2
- dbt_platform_helper/providers/semantic_version.py +126 -0
- dbt_platform_helper/providers/terraform_manifest.py +117 -26
- dbt_platform_helper/providers/validation.py +0 -14
- dbt_platform_helper/providers/version.py +36 -0
- dbt_platform_helper/providers/vpc.py +0 -5
- dbt_platform_helper/providers/yaml_file.py +5 -3
- dbt_platform_helper/utils/application.py +3 -2
- dbt_platform_helper/utils/versioning.py +152 -221
- {dbt_platform_helper-13.0.0.dist-info → dbt_platform_helper-13.0.2.dist-info}/METADATA +1 -1
- {dbt_platform_helper-13.0.0.dist-info → dbt_platform_helper-13.0.2.dist-info}/RECORD +26 -27
- platform_helper.py +2 -2
- dbt_platform_helper/domain/test_platform_terraform_manifest_generator.py +0 -100
- dbt_platform_helper/templates/environments/main.tf +0 -46
- dbt_platform_helper/utils/platform_config.py +0 -20
- {dbt_platform_helper-13.0.0.dist-info → dbt_platform_helper-13.0.2.dist-info}/LICENSE +0 -0
- {dbt_platform_helper-13.0.0.dist-info → dbt_platform_helper-13.0.2.dist-info}/WHEEL +0 -0
- {dbt_platform_helper-13.0.0.dist-info → dbt_platform_helper-13.0.2.dist-info}/entry_points.txt +0 -0
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
from os import makedirs
|
|
2
1
|
from pathlib import Path
|
|
3
2
|
|
|
4
3
|
|
|
@@ -8,19 +7,21 @@ class FileProvider:
|
|
|
8
7
|
pass
|
|
9
8
|
|
|
10
9
|
@staticmethod
|
|
11
|
-
def mkfile(base_path: str,
|
|
12
|
-
file_path = Path(
|
|
13
|
-
|
|
14
|
-
file_exists = file.exists()
|
|
15
|
-
|
|
16
|
-
if not file_path.parent.exists():
|
|
17
|
-
makedirs(file_path.parent)
|
|
18
|
-
|
|
10
|
+
def mkfile(base_path: str, file_name: str, contents: str, overwrite=False) -> str:
|
|
11
|
+
file_path = Path(base_path).joinpath(file_name)
|
|
12
|
+
file_exists = file_path.exists()
|
|
19
13
|
if file_exists and not overwrite:
|
|
20
14
|
return f"File {file_path} exists; doing nothing"
|
|
21
15
|
|
|
22
|
-
|
|
16
|
+
file_path.parent.mkdir(parents=True, exist_ok=True)
|
|
17
|
+
file_path.write_text(contents)
|
|
23
18
|
|
|
24
|
-
|
|
19
|
+
action = "overwritten" if file_exists and overwrite else "created"
|
|
20
|
+
return f"File {file_name} {action}"
|
|
25
21
|
|
|
26
|
-
|
|
22
|
+
@staticmethod
|
|
23
|
+
def delete_file(base_path: str, file_name: str):
|
|
24
|
+
file_path = Path(base_path) / file_name
|
|
25
|
+
if file_path.exists():
|
|
26
|
+
file_path.unlink()
|
|
27
|
+
return f"{str(file_path)} has been deleted"
|
|
@@ -21,7 +21,9 @@ def get_load_balancer_for_application(session: boto3.Session, app: str, env: str
|
|
|
21
21
|
load_balancer_arn = lb["ResourceArn"]
|
|
22
22
|
|
|
23
23
|
if not load_balancer_arn:
|
|
24
|
-
raise LoadBalancerNotFoundException(
|
|
24
|
+
raise LoadBalancerNotFoundException(
|
|
25
|
+
f"No load balancer found for {app} in the {env} environment"
|
|
26
|
+
)
|
|
25
27
|
|
|
26
28
|
return load_balancer_arn
|
|
27
29
|
|
|
@@ -39,7 +41,7 @@ def get_https_listener_for_application(session: boto3.Session, app: str, env: st
|
|
|
39
41
|
pass
|
|
40
42
|
|
|
41
43
|
if not listener_arn:
|
|
42
|
-
raise ListenerNotFoundException()
|
|
44
|
+
raise ListenerNotFoundException(f"No HTTPS listener for {app} in the {env} environment")
|
|
43
45
|
|
|
44
46
|
return listener_arn
|
|
45
47
|
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import re
|
|
2
|
+
from dataclasses import dataclass
|
|
3
|
+
from dataclasses import field
|
|
4
|
+
from typing import Dict
|
|
5
|
+
from typing import Optional
|
|
6
|
+
from typing import Union
|
|
7
|
+
|
|
8
|
+
from dbt_platform_helper.constants import PLATFORM_CONFIG_FILE
|
|
9
|
+
from dbt_platform_helper.constants import PLATFORM_HELPER_VERSION_FILE
|
|
10
|
+
from dbt_platform_helper.providers.validation import ValidationException
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class IncompatibleMajorVersionException(ValidationException):
|
|
14
|
+
def __init__(self, app_version: str, check_version: str):
|
|
15
|
+
super().__init__()
|
|
16
|
+
self.app_version = app_version
|
|
17
|
+
self.check_version = check_version
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class IncompatibleMinorVersionException(ValidationException):
|
|
21
|
+
def __init__(self, app_version: str, check_version: str):
|
|
22
|
+
super().__init__()
|
|
23
|
+
self.app_version = app_version
|
|
24
|
+
self.check_version = check_version
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class SemanticVersion:
|
|
28
|
+
def __init__(self, major, minor, patch):
|
|
29
|
+
self.major = major
|
|
30
|
+
self.minor = minor
|
|
31
|
+
self.patch = patch
|
|
32
|
+
|
|
33
|
+
def __str__(self) -> str:
|
|
34
|
+
if self.major is None:
|
|
35
|
+
return "unknown"
|
|
36
|
+
return ".".join([str(s) for s in [self.major, self.minor, self.patch]])
|
|
37
|
+
|
|
38
|
+
def __lt__(self, other) -> bool:
|
|
39
|
+
return (self.major, self.minor, self.patch) < (other.major, other.minor, other.patch)
|
|
40
|
+
|
|
41
|
+
def __eq__(self, other) -> bool:
|
|
42
|
+
return (self.major, self.minor, self.patch) == (other.major, other.minor, other.patch)
|
|
43
|
+
|
|
44
|
+
def validate_compatibility_with(self, other):
|
|
45
|
+
if (self.major == 0 and other.major == 0) and (
|
|
46
|
+
self.minor != other.minor or self.patch != other.patch
|
|
47
|
+
):
|
|
48
|
+
raise IncompatibleMajorVersionException(str(self), str(other))
|
|
49
|
+
|
|
50
|
+
if self.major != other.major:
|
|
51
|
+
raise IncompatibleMajorVersionException(str(self), str(other))
|
|
52
|
+
|
|
53
|
+
if self.minor != other.minor:
|
|
54
|
+
raise IncompatibleMinorVersionException(str(self), str(other))
|
|
55
|
+
|
|
56
|
+
@staticmethod
|
|
57
|
+
def from_string(version_string: Union[str, None]):
|
|
58
|
+
if version_string is None:
|
|
59
|
+
return None
|
|
60
|
+
|
|
61
|
+
version_plain = version_string.replace("v", "")
|
|
62
|
+
version_segments = re.split(r"[.\-]", version_plain)
|
|
63
|
+
|
|
64
|
+
if len(version_segments) != 3:
|
|
65
|
+
return None
|
|
66
|
+
|
|
67
|
+
output_version = [0, 0, 0]
|
|
68
|
+
for index, segment in enumerate(version_segments):
|
|
69
|
+
try:
|
|
70
|
+
output_version[index] = int(segment)
|
|
71
|
+
except ValueError:
|
|
72
|
+
output_version[index] = -1
|
|
73
|
+
|
|
74
|
+
return SemanticVersion(output_version[0], output_version[1], output_version[2])
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
class VersionStatus:
|
|
78
|
+
def __init__(
|
|
79
|
+
self, local_version: SemanticVersion = None, latest_release: SemanticVersion = None
|
|
80
|
+
):
|
|
81
|
+
self.local = local_version
|
|
82
|
+
self.latest = latest_release
|
|
83
|
+
|
|
84
|
+
def is_outdated(self):
|
|
85
|
+
return self.local != self.latest
|
|
86
|
+
|
|
87
|
+
def warn(self):
|
|
88
|
+
pass
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
@dataclass
|
|
92
|
+
class PlatformHelperVersionStatus(VersionStatus):
|
|
93
|
+
local: Optional[SemanticVersion] = None
|
|
94
|
+
latest: Optional[SemanticVersion] = None
|
|
95
|
+
deprecated_version_file: Optional[SemanticVersion] = None
|
|
96
|
+
platform_config_default: Optional[SemanticVersion] = None
|
|
97
|
+
pipeline_overrides: Optional[Dict[str, str]] = field(default_factory=dict)
|
|
98
|
+
|
|
99
|
+
def warn(self) -> dict:
|
|
100
|
+
if self.platform_config_default and not self.deprecated_version_file:
|
|
101
|
+
return {}
|
|
102
|
+
|
|
103
|
+
warnings = []
|
|
104
|
+
errors = []
|
|
105
|
+
|
|
106
|
+
missing_default_version_message = f"Create a section in the root of '{PLATFORM_CONFIG_FILE}':\n\ndefault_versions:\n platform-helper: "
|
|
107
|
+
deprecation_message = (
|
|
108
|
+
f"Please delete '{PLATFORM_HELPER_VERSION_FILE}' as it is now deprecated."
|
|
109
|
+
)
|
|
110
|
+
|
|
111
|
+
if self.platform_config_default and self.deprecated_version_file:
|
|
112
|
+
warnings.append(deprecation_message)
|
|
113
|
+
|
|
114
|
+
if not self.platform_config_default and self.deprecated_version_file:
|
|
115
|
+
warnings.append(deprecation_message)
|
|
116
|
+
warnings.append(f"{missing_default_version_message}{self.deprecated_version_file}\n")
|
|
117
|
+
|
|
118
|
+
if not self.platform_config_default and not self.deprecated_version_file:
|
|
119
|
+
message = f"Cannot get dbt-platform-helper version from '{PLATFORM_CONFIG_FILE}'.\n"
|
|
120
|
+
message += f"{missing_default_version_message}{self.local}\n"
|
|
121
|
+
errors.append(message)
|
|
122
|
+
|
|
123
|
+
return {
|
|
124
|
+
"warnings": warnings,
|
|
125
|
+
"errors": errors,
|
|
126
|
+
}
|
|
@@ -2,21 +2,20 @@ import json
|
|
|
2
2
|
from datetime import datetime
|
|
3
3
|
from importlib.metadata import version
|
|
4
4
|
from pathlib import Path
|
|
5
|
-
from typing import Callable
|
|
6
|
-
|
|
7
|
-
import click
|
|
8
5
|
|
|
9
6
|
from dbt_platform_helper.constants import SUPPORTED_AWS_PROVIDER_VERSION
|
|
10
7
|
from dbt_platform_helper.constants import SUPPORTED_TERRAFORM_VERSION
|
|
8
|
+
from dbt_platform_helper.providers.config import ConfigProvider
|
|
11
9
|
from dbt_platform_helper.providers.files import FileProvider
|
|
10
|
+
from dbt_platform_helper.providers.io import ClickIOProvider
|
|
12
11
|
|
|
13
12
|
|
|
14
13
|
class TerraformManifestProvider:
|
|
15
14
|
def __init__(
|
|
16
|
-
self, file_provider: FileProvider = FileProvider(),
|
|
15
|
+
self, file_provider: FileProvider = FileProvider(), io: ClickIOProvider = ClickIOProvider()
|
|
17
16
|
):
|
|
18
17
|
self.file_provider = file_provider
|
|
19
|
-
self.
|
|
18
|
+
self.io = io
|
|
20
19
|
|
|
21
20
|
def generate_codebase_pipeline_config(
|
|
22
21
|
self,
|
|
@@ -24,28 +23,50 @@ class TerraformManifestProvider:
|
|
|
24
23
|
terraform_platform_modules_version: str,
|
|
25
24
|
ecr_imports: dict[str, str],
|
|
26
25
|
):
|
|
27
|
-
default_account = (
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
.get("accounts", {})
|
|
31
|
-
.get("deploy", {})
|
|
32
|
-
.get("name")
|
|
33
|
-
)
|
|
26
|
+
default_account = self._get_account_for_env("*", platform_config)
|
|
27
|
+
state_key_suffix = f"{platform_config['application']}-codebase-pipelines"
|
|
28
|
+
|
|
34
29
|
terraform = {}
|
|
35
30
|
self._add_header(terraform)
|
|
36
|
-
self.
|
|
31
|
+
self._add_codebase_pipeline_locals(terraform)
|
|
37
32
|
self._add_provider(terraform, default_account)
|
|
38
|
-
self._add_backend(terraform, platform_config, default_account)
|
|
33
|
+
self._add_backend(terraform, platform_config, default_account, state_key_suffix)
|
|
39
34
|
self._add_codebase_pipeline_module(terraform, terraform_platform_modules_version)
|
|
40
35
|
self._add_imports(terraform, ecr_imports)
|
|
36
|
+
self._write_terraform_json(terraform, "terraform/codebase-pipelines")
|
|
41
37
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
38
|
+
def generate_environment_config(
|
|
39
|
+
self,
|
|
40
|
+
platform_config: dict,
|
|
41
|
+
env: str,
|
|
42
|
+
terraform_platform_modules_version: str,
|
|
43
|
+
):
|
|
44
|
+
platform_config = ConfigProvider.apply_environment_defaults(platform_config)
|
|
45
|
+
account = self._get_account_for_env(env, platform_config)
|
|
46
|
+
|
|
47
|
+
application_name = platform_config["application"]
|
|
48
|
+
state_key_suffix = f"{platform_config['application']}-{env}"
|
|
49
|
+
env_dir = f"terraform/environments/{env}"
|
|
50
|
+
|
|
51
|
+
terraform = {}
|
|
52
|
+
self._add_header(terraform)
|
|
53
|
+
self._add_environment_locals(terraform, application_name)
|
|
54
|
+
self._add_backend(terraform, platform_config, account, state_key_suffix)
|
|
55
|
+
self._add_extensions_module(terraform, terraform_platform_modules_version, env)
|
|
56
|
+
self._add_moved(terraform, platform_config)
|
|
57
|
+
self._ensure_no_hcl_manifest_file(env_dir)
|
|
58
|
+
self._write_terraform_json(terraform, env_dir)
|
|
59
|
+
|
|
60
|
+
@staticmethod
|
|
61
|
+
def _get_account_for_env(env, platform_config):
|
|
62
|
+
account = (
|
|
63
|
+
platform_config.get("environments", {})
|
|
64
|
+
.get(env, {})
|
|
65
|
+
.get("accounts", {})
|
|
66
|
+
.get("deploy", {})
|
|
67
|
+
.get("name")
|
|
47
68
|
)
|
|
48
|
-
|
|
69
|
+
return account
|
|
49
70
|
|
|
50
71
|
@staticmethod
|
|
51
72
|
def _add_header(terraform: dict):
|
|
@@ -56,7 +77,7 @@ class TerraformManifestProvider:
|
|
|
56
77
|
terraform["//"] = f"{version_header} {warning}"
|
|
57
78
|
|
|
58
79
|
@staticmethod
|
|
59
|
-
def
|
|
80
|
+
def _add_codebase_pipeline_locals(terraform: dict):
|
|
60
81
|
terraform["locals"] = {
|
|
61
82
|
"platform_config": '${yamldecode(file("../../platform-config.yml"))}',
|
|
62
83
|
"application": '${local.platform_config["application"]}',
|
|
@@ -73,17 +94,17 @@ class TerraformManifestProvider:
|
|
|
73
94
|
terraform["provider"]["aws"]["shared_credentials_files"] = ["~/.aws/config"]
|
|
74
95
|
|
|
75
96
|
@staticmethod
|
|
76
|
-
def _add_backend(terraform: dict, platform_config: dict,
|
|
97
|
+
def _add_backend(terraform: dict, platform_config: dict, account: str, state_key_suffix: str):
|
|
77
98
|
terraform["terraform"] = {
|
|
78
99
|
"required_version": SUPPORTED_TERRAFORM_VERSION,
|
|
79
100
|
"backend": {
|
|
80
101
|
"s3": {
|
|
81
|
-
"bucket": f"terraform-platform-state-{
|
|
82
|
-
"key": f"tfstate/application/{
|
|
102
|
+
"bucket": f"terraform-platform-state-{account}",
|
|
103
|
+
"key": f"tfstate/application/{state_key_suffix}.tfstate",
|
|
83
104
|
"region": "eu-west-2",
|
|
84
105
|
"encrypt": True,
|
|
85
|
-
"kms_key_id": f"alias/terraform-platform-state-s3-key-{
|
|
86
|
-
"dynamodb_table": f"terraform-platform-lockdb-{
|
|
106
|
+
"kms_key_id": f"alias/terraform-platform-state-s3-key-{account}",
|
|
107
|
+
"dynamodb_table": f"terraform-platform-lockdb-{account}",
|
|
87
108
|
}
|
|
88
109
|
},
|
|
89
110
|
"required_providers": {
|
|
@@ -110,6 +131,13 @@ class TerraformManifestProvider:
|
|
|
110
131
|
}
|
|
111
132
|
}
|
|
112
133
|
|
|
134
|
+
@staticmethod
|
|
135
|
+
def _add_extensions_module(terraform: dict, terraform_platform_modules_version: str, env: str):
|
|
136
|
+
source = f"git::https://github.com/uktrade/terraform-platform-modules.git//extensions?depth=1&ref={terraform_platform_modules_version}"
|
|
137
|
+
terraform["module"] = {
|
|
138
|
+
"extensions": {"source": source, "args": "${local.args}", "environment": env}
|
|
139
|
+
}
|
|
140
|
+
|
|
113
141
|
@staticmethod
|
|
114
142
|
def _add_imports(terraform: dict, ecr_imports: dict[str, str]):
|
|
115
143
|
if ecr_imports:
|
|
@@ -118,3 +146,66 @@ class TerraformManifestProvider:
|
|
|
118
146
|
"id": "${each.value}",
|
|
119
147
|
"to": "module.codebase-pipelines[each.key].aws_ecr_repository.this",
|
|
120
148
|
}
|
|
149
|
+
|
|
150
|
+
@staticmethod
|
|
151
|
+
def _add_environment_locals(terraform: dict, app: str):
|
|
152
|
+
terraform["locals"] = {
|
|
153
|
+
"config": '${yamldecode(file("../../../platform-config.yml"))}',
|
|
154
|
+
"environments": '${local.config["environments"]}',
|
|
155
|
+
"env_config": '${{for name, config in local.environments: name => merge(lookup(local.environments, "*", {}), config)}}',
|
|
156
|
+
"args": {
|
|
157
|
+
"application": app,
|
|
158
|
+
"services": '${local.config["extensions"]}',
|
|
159
|
+
"env_config": "${local.env_config}",
|
|
160
|
+
},
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
@staticmethod
|
|
164
|
+
def _add_moved(terraform, platform_config):
|
|
165
|
+
extensions_comment = "Moved extensions-tf to just extensions - this block tells terraform this. Can be removed once all services have moved to the new naming."
|
|
166
|
+
terraform["moved"] = [
|
|
167
|
+
{
|
|
168
|
+
"//": extensions_comment,
|
|
169
|
+
"from": "module.extensions-tf",
|
|
170
|
+
"to": "module.extensions",
|
|
171
|
+
}
|
|
172
|
+
]
|
|
173
|
+
|
|
174
|
+
extensions = platform_config.get("extensions", {})
|
|
175
|
+
s3_extension_names = [
|
|
176
|
+
extension_name
|
|
177
|
+
for extension_name, extension in extensions.items()
|
|
178
|
+
if extension["type"] == "s3"
|
|
179
|
+
]
|
|
180
|
+
s3_comment = "S3 bucket resources are now indexed. Can be removed once all services have moved to terraform-platform-modules 5.x."
|
|
181
|
+
|
|
182
|
+
for name in s3_extension_names:
|
|
183
|
+
resources = [
|
|
184
|
+
"aws_s3_bucket_server_side_encryption_configuration.encryption-config",
|
|
185
|
+
"aws_s3_bucket_policy.bucket-policy",
|
|
186
|
+
"aws_kms_key.kms-key",
|
|
187
|
+
"aws_kms_alias.s3-bucket",
|
|
188
|
+
]
|
|
189
|
+
moves = [f'module.extensions.module.s3["{name}"].{resource}' for resource in resources]
|
|
190
|
+
for move in moves:
|
|
191
|
+
terraform["moved"].append(
|
|
192
|
+
{
|
|
193
|
+
"//": s3_comment,
|
|
194
|
+
"from": move,
|
|
195
|
+
"to": f"{move}[0]",
|
|
196
|
+
}
|
|
197
|
+
)
|
|
198
|
+
|
|
199
|
+
def _write_terraform_json(self, terraform: dict, env_dir: str):
|
|
200
|
+
message = self.file_provider.mkfile(
|
|
201
|
+
str(Path(env_dir).absolute()),
|
|
202
|
+
"main.tf.json",
|
|
203
|
+
json.dumps(terraform, indent=2),
|
|
204
|
+
True,
|
|
205
|
+
)
|
|
206
|
+
self.io.info(message)
|
|
207
|
+
|
|
208
|
+
def _ensure_no_hcl_manifest_file(self, env_dir):
|
|
209
|
+
message = self.file_provider.delete_file(env_dir, "main.tf")
|
|
210
|
+
if message:
|
|
211
|
+
self.io.info(f"Manifest has moved to main.tf.json. {message}")
|
|
@@ -3,17 +3,3 @@ from dbt_platform_helper.platform_exception import PlatformException
|
|
|
3
3
|
|
|
4
4
|
class ValidationException(PlatformException):
|
|
5
5
|
pass
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
class IncompatibleMajorVersionException(ValidationException):
|
|
9
|
-
def __init__(self, app_version: str, check_version: str):
|
|
10
|
-
super().__init__()
|
|
11
|
-
self.app_version = app_version
|
|
12
|
-
self.check_version = check_version
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
class IncompatibleMinorVersionException(ValidationException):
|
|
16
|
-
def __init__(self, app_version: str, check_version: str):
|
|
17
|
-
super().__init__()
|
|
18
|
-
self.app_version = app_version
|
|
19
|
-
self.check_version = check_version
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
from abc import ABC
|
|
2
|
+
|
|
3
|
+
import requests
|
|
4
|
+
|
|
5
|
+
from dbt_platform_helper.providers.semantic_version import SemanticVersion
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class VersionProvider(ABC):
|
|
9
|
+
pass
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
# TODO add timeouts and exception handling for requests
|
|
13
|
+
# TODO Alternatively use the gitpython package?
|
|
14
|
+
class GithubVersionProvider(VersionProvider):
|
|
15
|
+
@staticmethod
|
|
16
|
+
def get_latest_version(repo_name: str, tags: bool = False) -> SemanticVersion:
|
|
17
|
+
if tags:
|
|
18
|
+
tags_list = requests.get(f"https://api.github.com/repos/{repo_name}/tags").json()
|
|
19
|
+
versions = [SemanticVersion.from_string(v["name"]) for v in tags_list]
|
|
20
|
+
versions.sort(reverse=True)
|
|
21
|
+
return versions[0]
|
|
22
|
+
|
|
23
|
+
package_info = requests.get(
|
|
24
|
+
f"https://api.github.com/repos/{repo_name}/releases/latest"
|
|
25
|
+
).json()
|
|
26
|
+
return SemanticVersion.from_string(package_info["tag_name"])
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class PyPiVersionProvider(VersionProvider):
|
|
30
|
+
@staticmethod
|
|
31
|
+
def get_latest_version(project_name: str) -> SemanticVersion:
|
|
32
|
+
package_info = requests.get(f"https://pypi.org/pypi/{project_name}/json").json()
|
|
33
|
+
released_versions = package_info["releases"].keys()
|
|
34
|
+
parsed_released_versions = [SemanticVersion.from_string(v) for v in released_versions]
|
|
35
|
+
parsed_released_versions.sort(reverse=True)
|
|
36
|
+
return parsed_released_versions[0]
|
|
@@ -98,9 +98,4 @@ class VpcProvider:
|
|
|
98
98
|
|
|
99
99
|
sec_groups = self._get_security_groups(app, env, vpc_id)
|
|
100
100
|
|
|
101
|
-
if not sec_groups:
|
|
102
|
-
raise SecurityGroupNotFoundException(
|
|
103
|
-
f"No matching security groups found in vpc '{vpc_name}'"
|
|
104
|
-
)
|
|
105
|
-
|
|
106
101
|
return Vpc(vpc_id, public_subnets, private_subnets, sec_groups)
|
|
@@ -19,11 +19,13 @@ class FileNotFoundException(FileProviderException):
|
|
|
19
19
|
|
|
20
20
|
|
|
21
21
|
class InvalidYamlException(YamlFileProviderException):
|
|
22
|
-
|
|
22
|
+
def __init__(self, path: str):
|
|
23
|
+
super().__init__(f"""{path} is not valid YAML.""")
|
|
23
24
|
|
|
24
25
|
|
|
25
26
|
class DuplicateKeysException(YamlFileProviderException):
|
|
26
|
-
|
|
27
|
+
def __init__(self, duplicate_keys: str):
|
|
28
|
+
super().__init__(f"""Duplicate keys found in your config file: {duplicate_keys}.""")
|
|
27
29
|
|
|
28
30
|
|
|
29
31
|
class YamlFileProvider:
|
|
@@ -39,7 +41,7 @@ class YamlFileProvider:
|
|
|
39
41
|
try:
|
|
40
42
|
yaml_content = yaml.safe_load(Path(path).read_text())
|
|
41
43
|
except ParserError:
|
|
42
|
-
raise InvalidYamlException(
|
|
44
|
+
raise InvalidYamlException(path)
|
|
43
45
|
|
|
44
46
|
if not yaml_content:
|
|
45
47
|
return {}
|
|
@@ -10,11 +10,11 @@ import boto3
|
|
|
10
10
|
|
|
11
11
|
from dbt_platform_helper.constants import PLATFORM_CONFIG_FILE
|
|
12
12
|
from dbt_platform_helper.platform_exception import PlatformException
|
|
13
|
+
from dbt_platform_helper.providers.config import ConfigProvider
|
|
13
14
|
from dbt_platform_helper.utils.aws import get_aws_session_or_abort
|
|
14
15
|
from dbt_platform_helper.utils.aws import get_profile_name_from_account_id
|
|
15
16
|
from dbt_platform_helper.utils.aws import get_ssm_secrets
|
|
16
17
|
from dbt_platform_helper.utils.messages import abort_with_error
|
|
17
|
-
from dbt_platform_helper.utils.platform_config import load_unvalidated_config_file
|
|
18
18
|
|
|
19
19
|
|
|
20
20
|
@dataclass
|
|
@@ -125,8 +125,9 @@ def load_application(app=None, default_session=None) -> Application:
|
|
|
125
125
|
|
|
126
126
|
def get_application_name(abort=abort_with_error):
|
|
127
127
|
if Path(PLATFORM_CONFIG_FILE).exists():
|
|
128
|
+
config = ConfigProvider()
|
|
128
129
|
try:
|
|
129
|
-
app_config = load_unvalidated_config_file()
|
|
130
|
+
app_config = config.load_unvalidated_config_file()
|
|
130
131
|
return app_config["application"]
|
|
131
132
|
except KeyError:
|
|
132
133
|
abort(
|