dbt-platform-helper 13.1.2__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/application.py +2 -4
- dbt_platform_helper/commands/codebase.py +11 -7
- dbt_platform_helper/commands/conduit.py +1 -3
- dbt_platform_helper/commands/config.py +12 -314
- dbt_platform_helper/commands/copilot.py +14 -8
- dbt_platform_helper/commands/database.py +17 -9
- dbt_platform_helper/commands/environment.py +5 -6
- dbt_platform_helper/commands/generate.py +2 -3
- dbt_platform_helper/commands/notify.py +1 -3
- dbt_platform_helper/commands/pipeline.py +1 -3
- dbt_platform_helper/commands/secrets.py +1 -3
- dbt_platform_helper/commands/version.py +2 -2
- dbt_platform_helper/domain/codebase.py +17 -9
- dbt_platform_helper/domain/config.py +345 -0
- dbt_platform_helper/domain/copilot.py +158 -157
- dbt_platform_helper/domain/maintenance_page.py +42 -15
- dbt_platform_helper/domain/pipelines.py +1 -1
- dbt_platform_helper/domain/terraform_environment.py +1 -1
- dbt_platform_helper/domain/versioning.py +161 -30
- dbt_platform_helper/providers/aws/__init__.py +0 -0
- dbt_platform_helper/providers/{aws.py → aws/exceptions.py} +10 -0
- dbt_platform_helper/providers/aws/interfaces.py +13 -0
- dbt_platform_helper/providers/aws/opensearch.py +23 -0
- dbt_platform_helper/providers/aws/redis.py +21 -0
- dbt_platform_helper/providers/aws/sso_auth.py +61 -0
- dbt_platform_helper/providers/cache.py +40 -4
- dbt_platform_helper/providers/config.py +2 -1
- dbt_platform_helper/providers/config_validator.py +28 -25
- dbt_platform_helper/providers/copilot.py +1 -1
- dbt_platform_helper/providers/io.py +5 -2
- dbt_platform_helper/providers/kms.py +22 -0
- dbt_platform_helper/providers/load_balancers.py +26 -15
- 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 +18 -88
- dbt_platform_helper/providers/terraform_manifest.py +1 -0
- dbt_platform_helper/providers/version.py +102 -26
- dbt_platform_helper/providers/version_status.py +80 -0
- dbt_platform_helper/providers/yaml_file.py +0 -1
- dbt_platform_helper/utils/aws.py +24 -142
- dbt_platform_helper/utils/git.py +3 -1
- dbt_platform_helper/utils/tool_versioning.py +12 -0
- {dbt_platform_helper-13.1.2.dist-info → dbt_platform_helper-13.3.0.dist-info}/METADATA +2 -2
- {dbt_platform_helper-13.1.2.dist-info → dbt_platform_helper-13.3.0.dist-info}/RECORD +48 -47
- {dbt_platform_helper-13.1.2.dist-info → dbt_platform_helper-13.3.0.dist-info}/WHEEL +1 -1
- platform_helper.py +1 -1
- dbt_platform_helper/providers/opensearch.py +0 -36
- dbt_platform_helper/providers/platform_helper_versioning.py +0 -107
- dbt_platform_helper/providers/redis.py +0 -34
- 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 -59
- dbt_platform_helper/utils/manifests.py +0 -18
- dbt_platform_helper/utils/versioning.py +0 -91
- {dbt_platform_helper-13.1.2.dist-info → dbt_platform_helper-13.3.0.dist-info}/LICENSE +0 -0
- {dbt_platform_helper-13.1.2.dist-info → dbt_platform_helper-13.3.0.dist-info}/entry_points.txt +0 -0
|
@@ -192,15 +192,14 @@ class LoadBalancerProvider:
|
|
|
192
192
|
rule_name: str,
|
|
193
193
|
priority: int,
|
|
194
194
|
conditions: list,
|
|
195
|
+
additional_tags: list = [],
|
|
195
196
|
):
|
|
196
197
|
return self.create_rule(
|
|
197
198
|
listener_arn=listener_arn,
|
|
198
199
|
priority=priority,
|
|
199
200
|
conditions=conditions,
|
|
200
201
|
actions=[{"Type": "forward", "TargetGroupArn": target_group_arn}],
|
|
201
|
-
tags=[
|
|
202
|
-
{"Key": "name", "Value": rule_name},
|
|
203
|
-
],
|
|
202
|
+
tags=[{"Key": "name", "Value": rule_name}, *additional_tags],
|
|
204
203
|
)
|
|
205
204
|
|
|
206
205
|
def create_header_rule(
|
|
@@ -212,6 +211,7 @@ class LoadBalancerProvider:
|
|
|
212
211
|
rule_name: str,
|
|
213
212
|
priority: int,
|
|
214
213
|
conditions: list,
|
|
214
|
+
additional_tags: list = [],
|
|
215
215
|
):
|
|
216
216
|
|
|
217
217
|
combined_conditions = [
|
|
@@ -222,11 +222,16 @@ class LoadBalancerProvider:
|
|
|
222
222
|
] + conditions
|
|
223
223
|
|
|
224
224
|
self.create_forward_rule(
|
|
225
|
-
listener_arn,
|
|
225
|
+
listener_arn,
|
|
226
|
+
target_group_arn,
|
|
227
|
+
rule_name,
|
|
228
|
+
priority,
|
|
229
|
+
combined_conditions,
|
|
230
|
+
additional_tags,
|
|
226
231
|
)
|
|
227
232
|
|
|
228
|
-
self.io.
|
|
229
|
-
f"Creating listener rule {rule_name} for HTTPS Listener with arn {listener_arn}.\
|
|
233
|
+
self.io.debug(
|
|
234
|
+
f"Creating listener rule {rule_name} for HTTPS Listener with arn {listener_arn}.\nIf request header {header_name} contains one of the values {values}, the request will be forwarded to target group with arn {target_group_arn}.\n\n",
|
|
230
235
|
)
|
|
231
236
|
|
|
232
237
|
def create_source_ip_rule(
|
|
@@ -237,6 +242,7 @@ class LoadBalancerProvider:
|
|
|
237
242
|
rule_name: str,
|
|
238
243
|
priority: int,
|
|
239
244
|
conditions: list,
|
|
245
|
+
additional_tags: list = [],
|
|
240
246
|
):
|
|
241
247
|
combined_conditions = [
|
|
242
248
|
{
|
|
@@ -246,24 +252,29 @@ class LoadBalancerProvider:
|
|
|
246
252
|
] + conditions
|
|
247
253
|
|
|
248
254
|
self.create_forward_rule(
|
|
249
|
-
listener_arn,
|
|
255
|
+
listener_arn,
|
|
256
|
+
target_group_arn,
|
|
257
|
+
rule_name,
|
|
258
|
+
priority,
|
|
259
|
+
combined_conditions,
|
|
260
|
+
additional_tags,
|
|
250
261
|
)
|
|
251
262
|
|
|
252
|
-
self.io.
|
|
253
|
-
f"Creating listener rule {rule_name} for HTTPS Listener with arn {listener_arn}.\
|
|
263
|
+
self.io.debug(
|
|
264
|
+
f"Creating listener rule {rule_name} for HTTPS Listener with arn {listener_arn}.\nIf request source ip matches one of the values {values}, the request will be forwarded to target group with arn {target_group_arn}.\n\n",
|
|
254
265
|
)
|
|
255
266
|
|
|
256
|
-
def delete_listener_rule_by_tags(self, tag_descriptions: list, tag_name: str) ->
|
|
257
|
-
|
|
267
|
+
def delete_listener_rule_by_tags(self, tag_descriptions: list, tag_name: str) -> list:
|
|
268
|
+
deleted_rules = []
|
|
258
269
|
|
|
259
270
|
for description in tag_descriptions:
|
|
260
271
|
tags = {t["Key"]: t["Value"] for t in description["Tags"]}
|
|
261
272
|
if tags.get("name") == tag_name:
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
273
|
+
if description["ResourceArn"]:
|
|
274
|
+
self.evlb_client.delete_rule(RuleArn=description["ResourceArn"])
|
|
275
|
+
deleted_rules.append(description)
|
|
265
276
|
|
|
266
|
-
return
|
|
277
|
+
return deleted_rules
|
|
267
278
|
|
|
268
279
|
|
|
269
280
|
class LoadBalancerException(PlatformException):
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import boto3
|
|
2
|
+
|
|
3
|
+
from dbt_platform_helper.platform_exception import PlatformException
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class ParameterStore:
|
|
7
|
+
def __init__(self, ssm_client: boto3.client):
|
|
8
|
+
self.ssm_client = ssm_client
|
|
9
|
+
|
|
10
|
+
def get_ssm_parameter_by_name(self, parameter_name: str) -> dict:
|
|
11
|
+
"""
|
|
12
|
+
Retrieves the latest version of a parameter from parameter store for a
|
|
13
|
+
given name/arn.
|
|
14
|
+
|
|
15
|
+
Args:
|
|
16
|
+
path (str): The parameter name to retrieve the parameter value for.
|
|
17
|
+
Returns:
|
|
18
|
+
dict: A dictionary representation of your ssm parameter
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
return self.ssm_client.get_parameter(Name=parameter_name)["Parameter"]
|
|
22
|
+
|
|
23
|
+
def get_ssm_parameters_by_path(self, path: str) -> list:
|
|
24
|
+
"""
|
|
25
|
+
Retrieves all SSM parameters for a given path from parameter store.
|
|
26
|
+
|
|
27
|
+
Args:
|
|
28
|
+
path (str): The parameter path to retrieve the parameters for. e.g. /copilot/applications/
|
|
29
|
+
Returns:
|
|
30
|
+
list: A list of dictionaries containing all SSM parameters under the provided path.
|
|
31
|
+
"""
|
|
32
|
+
|
|
33
|
+
parameters = []
|
|
34
|
+
paginator = self.ssm_client.get_paginator("get_parameters_by_path")
|
|
35
|
+
page_iterator = paginator.paginate(Path=path, Recursive=True)
|
|
36
|
+
|
|
37
|
+
for page in page_iterator:
|
|
38
|
+
parameters.extend(page.get("Parameters", []))
|
|
39
|
+
|
|
40
|
+
if parameters:
|
|
41
|
+
return parameters
|
|
42
|
+
else:
|
|
43
|
+
raise ParameterNotFoundForPathException()
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
class ParameterNotFoundForPathException(PlatformException):
|
|
47
|
+
"""Exception raised when no parameters are found for a given path."""
|
|
@@ -28,6 +28,7 @@ class PlatformConfigSchema:
|
|
|
28
28
|
PlatformConfigSchema.__monitoring_schema(),
|
|
29
29
|
PlatformConfigSchema.__opensearch_schema(),
|
|
30
30
|
PlatformConfigSchema.__postgres_schema(),
|
|
31
|
+
PlatformConfigSchema.__datadog_schema(),
|
|
31
32
|
PlatformConfigSchema.__prometheus_policy_schema(),
|
|
32
33
|
PlatformConfigSchema.__redis_schema(),
|
|
33
34
|
PlatformConfigSchema.__s3_bucket_schema(),
|
|
@@ -48,6 +49,7 @@ class PlatformConfigSchema:
|
|
|
48
49
|
"postgres": Schema(PlatformConfigSchema.__postgres_schema()),
|
|
49
50
|
"prometheus-policy": Schema(PlatformConfigSchema.__prometheus_policy_schema()),
|
|
50
51
|
"redis": Schema(PlatformConfigSchema.__redis_schema()),
|
|
52
|
+
"datadog": Schema(PlatformConfigSchema.__datadog_schema()),
|
|
51
53
|
"s3": Schema(PlatformConfigSchema.__s3_bucket_schema()),
|
|
52
54
|
"s3-policy": Schema(PlatformConfigSchema.__s3_bucket_policy_schema()),
|
|
53
55
|
"subscription-filter": PlatformConfigSchema.__no_configuration_required_schema(
|
|
@@ -434,6 +436,21 @@ class PlatformConfigSchema:
|
|
|
434
436
|
|
|
435
437
|
return True
|
|
436
438
|
|
|
439
|
+
@staticmethod
|
|
440
|
+
def __datadog_schema() -> dict:
|
|
441
|
+
return {
|
|
442
|
+
"type": "datadog",
|
|
443
|
+
Optional("environments"): {
|
|
444
|
+
Optional(PlatformConfigSchema.__valid_environment_name()): {
|
|
445
|
+
"team_name": str,
|
|
446
|
+
"contact_name": str,
|
|
447
|
+
"contact_email": str,
|
|
448
|
+
"documentation_url": str,
|
|
449
|
+
"services_to_monitor": list,
|
|
450
|
+
}
|
|
451
|
+
},
|
|
452
|
+
}
|
|
453
|
+
|
|
437
454
|
@staticmethod
|
|
438
455
|
def __s3_bucket_schema() -> dict:
|
|
439
456
|
def _valid_s3_bucket_arn(key):
|
|
@@ -1,12 +1,6 @@
|
|
|
1
1
|
import re
|
|
2
|
-
from dataclasses import dataclass
|
|
3
|
-
from dataclasses import field
|
|
4
|
-
from typing import Dict
|
|
5
|
-
from typing import Optional
|
|
6
2
|
from typing import Union
|
|
7
3
|
|
|
8
|
-
from dbt_platform_helper.constants import PLATFORM_CONFIG_FILE
|
|
9
|
-
from dbt_platform_helper.constants import PLATFORM_HELPER_VERSION_FILE
|
|
10
4
|
from dbt_platform_helper.providers.validation import ValidationException
|
|
11
5
|
|
|
12
6
|
|
|
@@ -35,13 +29,20 @@ class SemanticVersion:
|
|
|
35
29
|
return "unknown"
|
|
36
30
|
return ".".join([str(s) for s in [self.major, self.minor, self.patch]])
|
|
37
31
|
|
|
32
|
+
def __repr__(self) -> str:
|
|
33
|
+
return str(self)
|
|
34
|
+
|
|
38
35
|
def __lt__(self, other) -> bool:
|
|
39
36
|
return (self.major, self.minor, self.patch) < (other.major, other.minor, other.patch)
|
|
40
37
|
|
|
41
38
|
def __eq__(self, other) -> bool:
|
|
39
|
+
if other is None:
|
|
40
|
+
return False
|
|
42
41
|
return (self.major, self.minor, self.patch) == (other.major, other.minor, other.patch)
|
|
43
42
|
|
|
44
43
|
def validate_compatibility_with(self, other):
|
|
44
|
+
if other is None:
|
|
45
|
+
raise ValidationException("Cannot compare NoneType")
|
|
45
46
|
if (self.major == 0 and other.major == 0) and (
|
|
46
47
|
self.minor != other.minor or self.patch != other.patch
|
|
47
48
|
):
|
|
@@ -54,93 +55,22 @@ class SemanticVersion:
|
|
|
54
55
|
raise IncompatibleMinorVersionException(str(self), str(other))
|
|
55
56
|
|
|
56
57
|
@staticmethod
|
|
57
|
-
def
|
|
58
|
+
def _cast_to_int_with_fallback(input, fallback=-1):
|
|
59
|
+
try:
|
|
60
|
+
return int(input)
|
|
61
|
+
except ValueError:
|
|
62
|
+
return fallback
|
|
63
|
+
|
|
64
|
+
@classmethod
|
|
65
|
+
def from_string(self, version_string: Union[str, None]):
|
|
58
66
|
if version_string is None:
|
|
59
67
|
return None
|
|
60
68
|
|
|
61
|
-
|
|
62
|
-
version_segments = re.split(r"[.\-]", version_plain)
|
|
69
|
+
version_segments = re.split(r"[.\-]", version_string.replace("v", ""))
|
|
63
70
|
|
|
64
71
|
if len(version_segments) != 3:
|
|
65
72
|
return None
|
|
66
73
|
|
|
67
|
-
|
|
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
|
-
@dataclass
|
|
78
|
-
class VersionStatus:
|
|
79
|
-
local: SemanticVersion = None
|
|
80
|
-
latest: SemanticVersion = None
|
|
81
|
-
|
|
82
|
-
def __str__(self):
|
|
83
|
-
attrs = {
|
|
84
|
-
key: value for key, value in vars(self).items() if isinstance(value, SemanticVersion)
|
|
85
|
-
}
|
|
86
|
-
attrs_str = ", ".join(f"{key}: {value}" for key, value in attrs.items())
|
|
87
|
-
return f"{self.__class__.__name__}: {attrs_str}"
|
|
88
|
-
|
|
89
|
-
def is_outdated(self):
|
|
90
|
-
return self.local != self.latest
|
|
91
|
-
|
|
92
|
-
def validate(self):
|
|
93
|
-
pass
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
@dataclass
|
|
97
|
-
class PlatformHelperVersionStatus(VersionStatus):
|
|
98
|
-
local: Optional[SemanticVersion] = None
|
|
99
|
-
latest: Optional[SemanticVersion] = None
|
|
100
|
-
deprecated_version_file: Optional[SemanticVersion] = None
|
|
101
|
-
platform_config_default: Optional[SemanticVersion] = None
|
|
102
|
-
pipeline_overrides: Optional[Dict[str, str]] = field(default_factory=dict)
|
|
103
|
-
|
|
104
|
-
def __str__(self):
|
|
105
|
-
semantic_version_attrs = {
|
|
106
|
-
key: value for key, value in vars(self).items() if isinstance(value, SemanticVersion)
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
class_str = ", ".join(f"{key}: {value}" for key, value in semantic_version_attrs.items())
|
|
110
|
-
|
|
111
|
-
if self.pipeline_overrides.items():
|
|
112
|
-
pipeline_overrides_str = "pipeline_overrides: " + ", ".join(
|
|
113
|
-
f"{key}: {value}" for key, value in self.pipeline_overrides.items()
|
|
114
|
-
)
|
|
115
|
-
class_str = ", ".join([class_str, pipeline_overrides_str])
|
|
116
|
-
|
|
117
|
-
return f"{self.__class__.__name__}: {class_str}"
|
|
118
|
-
|
|
119
|
-
def validate(self) -> dict:
|
|
120
|
-
if self.platform_config_default and not self.deprecated_version_file:
|
|
121
|
-
return {}
|
|
122
|
-
|
|
123
|
-
warnings = []
|
|
124
|
-
errors = []
|
|
125
|
-
|
|
126
|
-
missing_default_version_message = f"Create a section in the root of '{PLATFORM_CONFIG_FILE}':\n\ndefault_versions:\n platform-helper: "
|
|
127
|
-
deprecation_message = (
|
|
128
|
-
f"Please delete '{PLATFORM_HELPER_VERSION_FILE}' as it is now deprecated."
|
|
129
|
-
)
|
|
130
|
-
|
|
131
|
-
if self.platform_config_default and self.deprecated_version_file:
|
|
132
|
-
warnings.append(deprecation_message)
|
|
133
|
-
|
|
134
|
-
if not self.platform_config_default and self.deprecated_version_file:
|
|
135
|
-
warnings.append(deprecation_message)
|
|
136
|
-
warnings.append(f"{missing_default_version_message}{self.deprecated_version_file}\n")
|
|
137
|
-
|
|
138
|
-
if not self.platform_config_default and not self.deprecated_version_file:
|
|
139
|
-
message = f"Cannot get dbt-platform-helper version from '{PLATFORM_CONFIG_FILE}'.\n"
|
|
140
|
-
message += f"{missing_default_version_message}{self.local}\n"
|
|
141
|
-
errors.append(message)
|
|
74
|
+
major, minor, patch = [self._cast_to_int_with_fallback(s) for s in version_segments]
|
|
142
75
|
|
|
143
|
-
return
|
|
144
|
-
"warnings": warnings,
|
|
145
|
-
"errors": errors,
|
|
146
|
-
}
|
|
76
|
+
return SemanticVersion(major, minor, patch)
|
|
@@ -128,6 +128,7 @@ class TerraformManifestProvider:
|
|
|
128
128
|
"codebase": "${each.key}",
|
|
129
129
|
"repository": "${each.value.repository}",
|
|
130
130
|
"deploy_repository": f"{deploy_repository}",
|
|
131
|
+
"deploy_repository_branch": '${lookup(each.value, "deploy_repository_branch", "main")}',
|
|
131
132
|
"additional_ecr_repository": '${lookup(each.value, "additional_ecr_repository", null)}',
|
|
132
133
|
"pipelines": '${lookup(each.value, "pipelines", [])}',
|
|
133
134
|
"services": "${each.value.services}",
|
|
@@ -1,18 +1,36 @@
|
|
|
1
|
+
import re
|
|
2
|
+
import subprocess
|
|
1
3
|
from abc import ABC
|
|
4
|
+
from abc import abstractmethod
|
|
2
5
|
from importlib.metadata import PackageNotFoundError
|
|
3
6
|
from importlib.metadata import version
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
from typing import Union
|
|
4
9
|
|
|
5
|
-
import
|
|
10
|
+
from requests import Session
|
|
11
|
+
from requests.adapters import HTTPAdapter
|
|
12
|
+
from urllib3.util import Retry
|
|
6
13
|
|
|
14
|
+
from dbt_platform_helper.constants import PLATFORM_HELPER_VERSION_FILE
|
|
7
15
|
from dbt_platform_helper.platform_exception import PlatformException
|
|
16
|
+
from dbt_platform_helper.providers.io import ClickIOProvider
|
|
8
17
|
from dbt_platform_helper.providers.semantic_version import SemanticVersion
|
|
18
|
+
from dbt_platform_helper.providers.yaml_file import FileProviderException
|
|
19
|
+
from dbt_platform_helper.providers.yaml_file import YamlFileProvider
|
|
9
20
|
|
|
10
21
|
|
|
11
|
-
|
|
22
|
+
def set_up_retry():
|
|
23
|
+
session = Session()
|
|
24
|
+
retries = Retry(total=5, backoff_factor=0.1, status_forcelist=[403, 500, 502, 503, 504])
|
|
25
|
+
session.mount("https://", HTTPAdapter(max_retries=retries))
|
|
26
|
+
return session
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class InstalledVersionProviderException(PlatformException):
|
|
12
30
|
pass
|
|
13
31
|
|
|
14
32
|
|
|
15
|
-
class InstalledToolNotFoundException(
|
|
33
|
+
class InstalledToolNotFoundException(InstalledVersionProviderException):
|
|
16
34
|
def __init__(
|
|
17
35
|
self,
|
|
18
36
|
tool_name: str,
|
|
@@ -21,40 +39,98 @@ class InstalledToolNotFoundException(LocalVersionProviderException):
|
|
|
21
39
|
|
|
22
40
|
|
|
23
41
|
class VersionProvider(ABC):
|
|
24
|
-
|
|
42
|
+
@abstractmethod
|
|
43
|
+
def get_semantic_version() -> SemanticVersion:
|
|
44
|
+
raise NotImplementedError("Must be implemented in subclasses")
|
|
25
45
|
|
|
26
46
|
|
|
27
|
-
class
|
|
47
|
+
class InstalledVersionProvider:
|
|
28
48
|
@staticmethod
|
|
29
|
-
def
|
|
49
|
+
def get_semantic_version(tool_name: str) -> SemanticVersion:
|
|
30
50
|
try:
|
|
31
51
|
return SemanticVersion.from_string(version(tool_name))
|
|
32
52
|
except PackageNotFoundError:
|
|
33
53
|
raise InstalledToolNotFoundException(tool_name)
|
|
34
54
|
|
|
35
55
|
|
|
36
|
-
|
|
37
|
-
# TODO Alternatively use the gitpython package?
|
|
38
|
-
class GithubVersionProvider(VersionProvider):
|
|
56
|
+
class GithubLatestVersionProvider(VersionProvider):
|
|
39
57
|
@staticmethod
|
|
40
|
-
def
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
58
|
+
def get_semantic_version(
|
|
59
|
+
repo_name: str, tags: bool = False, request_session=set_up_retry(), io=ClickIOProvider()
|
|
60
|
+
) -> Union[SemanticVersion, None]:
|
|
61
|
+
|
|
62
|
+
semantic_version = None
|
|
63
|
+
try:
|
|
64
|
+
if tags:
|
|
65
|
+
response = request_session.get(f"https://api.github.com/repos/{repo_name}/tags")
|
|
66
|
+
|
|
67
|
+
versions = [SemanticVersion.from_string(v["name"]) for v in response.json()]
|
|
68
|
+
versions.sort(reverse=True)
|
|
69
|
+
semantic_version = versions[0]
|
|
70
|
+
else:
|
|
71
|
+
package_info = request_session.get(
|
|
72
|
+
f"https://api.github.com/repos/{repo_name}/releases/latest"
|
|
73
|
+
).json()
|
|
74
|
+
semantic_version = SemanticVersion.from_string(package_info["tag_name"])
|
|
75
|
+
except Exception as e:
|
|
76
|
+
io.error(f"Exception occured when calling Github with:\n{str(e)}")
|
|
46
77
|
|
|
47
|
-
|
|
48
|
-
f"https://api.github.com/repos/{repo_name}/releases/latest"
|
|
49
|
-
).json()
|
|
50
|
-
return SemanticVersion.from_string(package_info["tag_name"])
|
|
78
|
+
return semantic_version
|
|
51
79
|
|
|
52
80
|
|
|
53
|
-
class
|
|
81
|
+
class PyPiLatestVersionProvider(VersionProvider):
|
|
54
82
|
@staticmethod
|
|
55
|
-
def
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
83
|
+
def get_semantic_version(
|
|
84
|
+
project_name: str, request_session=set_up_retry(), io=ClickIOProvider()
|
|
85
|
+
) -> Union[SemanticVersion, None]:
|
|
86
|
+
semantic_version = None
|
|
87
|
+
try:
|
|
88
|
+
package_info = request_session.get(f"https://pypi.org/pypi/{project_name}/json").json()
|
|
89
|
+
released_versions = package_info["releases"].keys()
|
|
90
|
+
parsed_released_versions = [SemanticVersion.from_string(v) for v in released_versions]
|
|
91
|
+
parsed_released_versions.sort(reverse=True)
|
|
92
|
+
semantic_version = parsed_released_versions[0]
|
|
93
|
+
except Exception as e:
|
|
94
|
+
io.error(f"Exception occured when calling PyPi with:\n{str(e)}")
|
|
95
|
+
return semantic_version
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
class DeprecatedVersionFileVersionProvider(VersionProvider):
|
|
99
|
+
def __init__(self, file_provider: YamlFileProvider):
|
|
100
|
+
self.file_provider = file_provider or YamlFileProvider
|
|
101
|
+
|
|
102
|
+
def get_semantic_version(self) -> Union[SemanticVersion, None]:
|
|
103
|
+
deprecated_version_file = Path(PLATFORM_HELPER_VERSION_FILE)
|
|
104
|
+
try:
|
|
105
|
+
loaded_version = self.file_provider.load(deprecated_version_file)
|
|
106
|
+
version_from_file = SemanticVersion.from_string(loaded_version)
|
|
107
|
+
except FileProviderException:
|
|
108
|
+
version_from_file = None
|
|
109
|
+
return version_from_file
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
class AWSCLIInstalledVersionProvider(VersionProvider):
|
|
113
|
+
@staticmethod
|
|
114
|
+
def get_semantic_version() -> Union[SemanticVersion, None]:
|
|
115
|
+
installed_aws_version = None
|
|
116
|
+
try:
|
|
117
|
+
response = subprocess.run("aws --version", capture_output=True, shell=True)
|
|
118
|
+
matched = re.match(r"aws-cli/([0-9.]+)", response.stdout.decode("utf8"))
|
|
119
|
+
installed_aws_version = matched.group(1)
|
|
120
|
+
except (ValueError, AttributeError):
|
|
121
|
+
pass
|
|
122
|
+
return SemanticVersion.from_string(installed_aws_version)
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
class CopilotInstalledVersionProvider(VersionProvider):
|
|
126
|
+
@staticmethod
|
|
127
|
+
def get_semantic_version() -> Union[SemanticVersion, None]:
|
|
128
|
+
copilot_version = None
|
|
129
|
+
|
|
130
|
+
try:
|
|
131
|
+
response = subprocess.run("copilot --version", capture_output=True, shell=True)
|
|
132
|
+
[copilot_version] = re.findall(r"[0-9.]+", response.stdout.decode("utf8"))
|
|
133
|
+
except ValueError:
|
|
134
|
+
pass
|
|
135
|
+
|
|
136
|
+
return SemanticVersion.from_string(copilot_version)
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
from dataclasses import dataclass
|
|
2
|
+
from dataclasses import field
|
|
3
|
+
from typing import Dict
|
|
4
|
+
from typing import Optional
|
|
5
|
+
|
|
6
|
+
from dbt_platform_helper.constants import PLATFORM_CONFIG_FILE
|
|
7
|
+
from dbt_platform_helper.constants import PLATFORM_HELPER_VERSION_FILE
|
|
8
|
+
from dbt_platform_helper.providers.semantic_version import SemanticVersion
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@dataclass
|
|
12
|
+
class VersionStatus:
|
|
13
|
+
installed: SemanticVersion = None
|
|
14
|
+
latest: SemanticVersion = None
|
|
15
|
+
|
|
16
|
+
def __str__(self):
|
|
17
|
+
attrs = {
|
|
18
|
+
key: value for key, value in vars(self).items() if isinstance(value, SemanticVersion)
|
|
19
|
+
}
|
|
20
|
+
attrs_str = ", ".join(f"{key}: {value}" for key, value in attrs.items())
|
|
21
|
+
return f"{self.__class__.__name__}: {attrs_str}"
|
|
22
|
+
|
|
23
|
+
def is_outdated(self):
|
|
24
|
+
return self.installed != self.latest
|
|
25
|
+
|
|
26
|
+
def validate(self):
|
|
27
|
+
pass
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
@dataclass
|
|
31
|
+
class PlatformHelperVersionStatus(VersionStatus):
|
|
32
|
+
installed: Optional[SemanticVersion] = None
|
|
33
|
+
latest: Optional[SemanticVersion] = None
|
|
34
|
+
deprecated_version_file: Optional[SemanticVersion] = None
|
|
35
|
+
platform_config_default: Optional[SemanticVersion] = None
|
|
36
|
+
pipeline_overrides: Optional[Dict[str, str]] = field(default_factory=dict)
|
|
37
|
+
|
|
38
|
+
def __str__(self):
|
|
39
|
+
semantic_version_attrs = {
|
|
40
|
+
key: value for key, value in vars(self).items() if isinstance(value, SemanticVersion)
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
class_str = ", ".join(f"{key}: {value}" for key, value in semantic_version_attrs.items())
|
|
44
|
+
|
|
45
|
+
if self.pipeline_overrides.items():
|
|
46
|
+
pipeline_overrides_str = "pipeline_overrides: " + ", ".join(
|
|
47
|
+
f"{key}: {value}" for key, value in self.pipeline_overrides.items()
|
|
48
|
+
)
|
|
49
|
+
class_str = ", ".join([class_str, pipeline_overrides_str])
|
|
50
|
+
|
|
51
|
+
return f"{self.__class__.__name__}: {class_str}"
|
|
52
|
+
|
|
53
|
+
def validate(self) -> dict:
|
|
54
|
+
if self.platform_config_default and not self.deprecated_version_file:
|
|
55
|
+
return {}
|
|
56
|
+
|
|
57
|
+
warnings = []
|
|
58
|
+
errors = []
|
|
59
|
+
|
|
60
|
+
missing_default_version_message = f"Create a section in the root of '{PLATFORM_CONFIG_FILE}':\n\ndefault_versions:\n platform-helper: "
|
|
61
|
+
deprecation_message = (
|
|
62
|
+
f"Please delete '{PLATFORM_HELPER_VERSION_FILE}' as it is now deprecated."
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
if self.platform_config_default and self.deprecated_version_file:
|
|
66
|
+
warnings.append(deprecation_message)
|
|
67
|
+
|
|
68
|
+
if not self.platform_config_default and self.deprecated_version_file:
|
|
69
|
+
warnings.append(deprecation_message)
|
|
70
|
+
warnings.append(f"{missing_default_version_message}{self.deprecated_version_file}\n")
|
|
71
|
+
|
|
72
|
+
if not self.platform_config_default and not self.deprecated_version_file:
|
|
73
|
+
message = f"Cannot get dbt-platform-helper version from '{PLATFORM_CONFIG_FILE}'.\n"
|
|
74
|
+
message += f"{missing_default_version_message}{self.installed}\n"
|
|
75
|
+
errors.append(message)
|
|
76
|
+
|
|
77
|
+
return {
|
|
78
|
+
"warnings": warnings,
|
|
79
|
+
"errors": errors,
|
|
80
|
+
}
|