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
|
@@ -1,11 +1,33 @@
|
|
|
1
|
+
import os
|
|
2
|
+
|
|
1
3
|
from dbt_platform_helper.platform_exception import PlatformException
|
|
4
|
+
from dbt_platform_helper.providers.config import ConfigProvider
|
|
2
5
|
from dbt_platform_helper.providers.io import ClickIOProvider
|
|
3
|
-
from dbt_platform_helper.providers.
|
|
4
|
-
|
|
6
|
+
from dbt_platform_helper.providers.semantic_version import (
|
|
7
|
+
IncompatibleMajorVersionException,
|
|
8
|
+
)
|
|
9
|
+
from dbt_platform_helper.providers.semantic_version import (
|
|
10
|
+
IncompatibleMinorVersionException,
|
|
5
11
|
)
|
|
6
|
-
from dbt_platform_helper.providers.semantic_version import PlatformHelperVersionStatus
|
|
7
12
|
from dbt_platform_helper.providers.semantic_version import SemanticVersion
|
|
8
|
-
from dbt_platform_helper.
|
|
13
|
+
from dbt_platform_helper.providers.version import AWSCLIInstalledVersionProvider
|
|
14
|
+
from dbt_platform_helper.providers.version import CopilotInstalledVersionProvider
|
|
15
|
+
from dbt_platform_helper.providers.version import DeprecatedVersionFileVersionProvider
|
|
16
|
+
from dbt_platform_helper.providers.version import GithubLatestVersionProvider
|
|
17
|
+
from dbt_platform_helper.providers.version import InstalledVersionProvider
|
|
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
|
|
22
|
+
from dbt_platform_helper.providers.yaml_file import YamlFileProvider
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def running_as_installed_package():
|
|
26
|
+
return "site-packages" in __file__
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def skip_version_checks():
|
|
30
|
+
return not running_as_installed_package() or "PLATFORM_TOOLS_SKIP_VERSION_CHECK" in os.environ
|
|
9
31
|
|
|
10
32
|
|
|
11
33
|
class PlatformHelperVersionNotFoundException(PlatformException):
|
|
@@ -13,14 +35,113 @@ class PlatformHelperVersionNotFoundException(PlatformException):
|
|
|
13
35
|
super().__init__(f"""Platform helper version could not be resolved.""")
|
|
14
36
|
|
|
15
37
|
|
|
16
|
-
class
|
|
17
|
-
def __init__(
|
|
18
|
-
self
|
|
19
|
-
|
|
20
|
-
|
|
38
|
+
class PlatformHelperVersioning:
|
|
39
|
+
def __init__(
|
|
40
|
+
self,
|
|
41
|
+
io: ClickIOProvider = ClickIOProvider(),
|
|
42
|
+
version_file_version_provider: DeprecatedVersionFileVersionProvider = DeprecatedVersionFileVersionProvider(
|
|
43
|
+
YamlFileProvider
|
|
44
|
+
),
|
|
45
|
+
config_provider: ConfigProvider = ConfigProvider(),
|
|
46
|
+
latest_version_provider: VersionProvider = PyPiLatestVersionProvider,
|
|
47
|
+
installed_version_provider: InstalledVersionProvider = InstalledVersionProvider(),
|
|
48
|
+
skip_versioning_checks: bool = None,
|
|
49
|
+
):
|
|
50
|
+
self.io = io
|
|
51
|
+
self.version_file_version_provider = version_file_version_provider
|
|
52
|
+
self.config_provider = config_provider
|
|
53
|
+
self.latest_version_provider = latest_version_provider
|
|
54
|
+
self.installed_version_provider = installed_version_provider
|
|
55
|
+
self.skip_versioning_checks = (
|
|
56
|
+
skip_versioning_checks if skip_versioning_checks is not None else skip_version_checks()
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
def get_required_version(self, pipeline=None):
|
|
60
|
+
version_status = self._get_version_status()
|
|
61
|
+
self.io.process_messages(version_status.validate())
|
|
62
|
+
required_version = self._resolve_required_version(pipeline, version_status)
|
|
63
|
+
self.io.info(required_version)
|
|
64
|
+
return required_version
|
|
65
|
+
|
|
66
|
+
# Used in the generate command
|
|
67
|
+
def check_platform_helper_version_mismatch(self):
|
|
68
|
+
if self.skip_versioning_checks:
|
|
69
|
+
return
|
|
70
|
+
|
|
71
|
+
version_status = self._get_version_status()
|
|
72
|
+
self.io.process_messages(version_status.validate())
|
|
73
|
+
|
|
74
|
+
required_version = SemanticVersion.from_string(
|
|
75
|
+
self._resolve_required_version(version_status=version_status)
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
if not version_status.installed == required_version:
|
|
79
|
+
message = (
|
|
80
|
+
f"WARNING: You are running platform-helper v{version_status.installed} against "
|
|
81
|
+
f"v{required_version} specified for the project."
|
|
82
|
+
)
|
|
83
|
+
self.io.warn(message)
|
|
84
|
+
|
|
85
|
+
def check_if_needs_update(self):
|
|
86
|
+
if self.skip_versioning_checks:
|
|
87
|
+
return
|
|
88
|
+
|
|
89
|
+
version_status = self._get_version_status(include_project_versions=False)
|
|
90
|
+
|
|
91
|
+
message = (
|
|
92
|
+
f"You are running platform-helper v{version_status.installed}, upgrade to "
|
|
93
|
+
f"v{version_status.latest} by running run `pip install "
|
|
94
|
+
"--upgrade dbt-platform-helper`."
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
try:
|
|
98
|
+
version_status.installed.validate_compatibility_with(version_status.latest)
|
|
99
|
+
except IncompatibleMajorVersionException:
|
|
100
|
+
self.io.error(message)
|
|
101
|
+
except IncompatibleMinorVersionException:
|
|
102
|
+
self.io.warn(message)
|
|
103
|
+
|
|
104
|
+
def _get_version_status(
|
|
105
|
+
self,
|
|
106
|
+
include_project_versions: bool = True,
|
|
107
|
+
) -> PlatformHelperVersionStatus:
|
|
108
|
+
locally_installed_version = self.installed_version_provider.get_semantic_version(
|
|
109
|
+
"dbt-platform-helper"
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
latest_release = self.latest_version_provider.get_semantic_version("dbt-platform-helper")
|
|
113
|
+
|
|
114
|
+
if not include_project_versions:
|
|
115
|
+
return PlatformHelperVersionStatus(
|
|
116
|
+
installed=locally_installed_version,
|
|
117
|
+
latest=latest_release,
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
platform_config_default, pipeline_overrides = None, {}
|
|
121
|
+
|
|
122
|
+
platform_config = self.config_provider.load_unvalidated_config_file()
|
|
123
|
+
|
|
124
|
+
if platform_config:
|
|
125
|
+
platform_config_default = SemanticVersion.from_string(
|
|
126
|
+
platform_config.get("default_versions", {}).get("platform-helper")
|
|
127
|
+
)
|
|
128
|
+
|
|
129
|
+
pipeline_overrides = {
|
|
130
|
+
name: pipeline.get("versions", {}).get("platform-helper")
|
|
131
|
+
for name, pipeline in platform_config.get("environment_pipelines", {}).items()
|
|
132
|
+
if pipeline.get("versions", {}).get("platform-helper")
|
|
133
|
+
}
|
|
134
|
+
out = PlatformHelperVersionStatus(
|
|
135
|
+
installed=locally_installed_version,
|
|
136
|
+
latest=latest_release,
|
|
137
|
+
deprecated_version_file=self.version_file_version_provider.get_semantic_version(),
|
|
138
|
+
platform_config_default=platform_config_default,
|
|
139
|
+
pipeline_overrides=pipeline_overrides,
|
|
21
140
|
)
|
|
22
141
|
|
|
23
|
-
|
|
142
|
+
return out
|
|
143
|
+
|
|
144
|
+
def _resolve_required_version(
|
|
24
145
|
self, pipeline: str = None, version_status: PlatformHelperVersionStatus = None
|
|
25
146
|
) -> str:
|
|
26
147
|
pipeline_version = version_status.pipeline_overrides.get(pipeline)
|
|
@@ -40,28 +161,38 @@ class RequiredVersion:
|
|
|
40
161
|
|
|
41
162
|
return out
|
|
42
163
|
|
|
43
|
-
def get_required_version(self, pipeline=None):
|
|
44
|
-
version_status = self.platform_helper_versioning.get_status()
|
|
45
|
-
self.io.process_messages(version_status.validate())
|
|
46
|
-
required_version = self.get_required_platform_helper_version(pipeline, version_status)
|
|
47
|
-
self.io.info(required_version)
|
|
48
|
-
return required_version
|
|
49
164
|
|
|
50
|
-
|
|
51
|
-
def
|
|
52
|
-
|
|
53
|
-
|
|
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
|
+
)
|
|
54
175
|
|
|
55
|
-
|
|
56
|
-
|
|
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
|
+
)
|
|
57
181
|
|
|
58
|
-
|
|
59
|
-
|
|
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
|
|
60
192
|
)
|
|
61
193
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
self.io.warn(message)
|
|
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,13 @@
|
|
|
1
|
+
from typing import Protocol
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class GetVersionsProtocol(Protocol):
|
|
5
|
+
def get_supported_versions(self) -> list[str]: ...
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class GetReferenceProtocol(Protocol):
|
|
9
|
+
def get_reference(self) -> str: ...
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class AwsGetVersionProtocol(GetReferenceProtocol, GetVersionsProtocol):
|
|
13
|
+
pass
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import boto3
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class Opensearch:
|
|
5
|
+
|
|
6
|
+
def __init__(self, client: boto3.client):
|
|
7
|
+
self.client = client
|
|
8
|
+
self.engine = "OpenSearch"
|
|
9
|
+
|
|
10
|
+
def get_reference(self) -> str:
|
|
11
|
+
return self.engine.lower()
|
|
12
|
+
|
|
13
|
+
def get_supported_versions(self) -> list[str]:
|
|
14
|
+
response = self.client.list_versions()
|
|
15
|
+
all_versions = response["Versions"]
|
|
16
|
+
|
|
17
|
+
supported_versions = [
|
|
18
|
+
version.removeprefix(f"{self.engine}_")
|
|
19
|
+
for version in all_versions
|
|
20
|
+
if version.startswith(f"{self.engine}_")
|
|
21
|
+
]
|
|
22
|
+
|
|
23
|
+
return supported_versions
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import boto3
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class Redis:
|
|
5
|
+
|
|
6
|
+
def __init__(self, client: boto3.client):
|
|
7
|
+
self.client = client
|
|
8
|
+
self.engine = "redis"
|
|
9
|
+
|
|
10
|
+
def get_reference(self) -> str:
|
|
11
|
+
return self.engine.lower()
|
|
12
|
+
|
|
13
|
+
def get_supported_versions(self) -> list[str]:
|
|
14
|
+
supported_versions_response = self.client.describe_cache_engine_versions(Engine=self.engine)
|
|
15
|
+
|
|
16
|
+
supported_versions = [
|
|
17
|
+
version["EngineVersion"]
|
|
18
|
+
for version in supported_versions_response["CacheEngineVersions"]
|
|
19
|
+
]
|
|
20
|
+
|
|
21
|
+
return supported_versions
|
|
@@ -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)
|
|
@@ -1,10 +1,34 @@
|
|
|
1
1
|
import os
|
|
2
|
+
from abc import ABC
|
|
3
|
+
from abc import abstractmethod
|
|
2
4
|
from datetime import datetime
|
|
3
5
|
|
|
6
|
+
from dbt_platform_helper.providers.aws.interfaces import AwsGetVersionProtocol
|
|
4
7
|
from dbt_platform_helper.providers.yaml_file import YamlFileProvider
|
|
5
8
|
|
|
6
9
|
|
|
7
|
-
class
|
|
10
|
+
class GetDataStrategy(ABC):
|
|
11
|
+
@abstractmethod
|
|
12
|
+
def retrieve_fresh_data(self):
|
|
13
|
+
pass
|
|
14
|
+
|
|
15
|
+
@abstractmethod
|
|
16
|
+
def get_data_identifier(self):
|
|
17
|
+
pass
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class GetAWSVersionStrategy(GetDataStrategy):
|
|
21
|
+
def __init__(self, client_provider: AwsGetVersionProtocol):
|
|
22
|
+
self.client_provider = client_provider
|
|
23
|
+
|
|
24
|
+
def retrieve_fresh_data(self):
|
|
25
|
+
return self.client_provider.get_supported_versions()
|
|
26
|
+
|
|
27
|
+
def get_data_identifier(self):
|
|
28
|
+
return self.client_provider.get_reference()
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class Cache:
|
|
8
32
|
def __init__(
|
|
9
33
|
self,
|
|
10
34
|
file_provider: YamlFileProvider = None,
|
|
@@ -12,13 +36,25 @@ class CacheProvider:
|
|
|
12
36
|
self._cache_file = ".platform-helper-config-cache.yml"
|
|
13
37
|
self.file_provider = file_provider or YamlFileProvider
|
|
14
38
|
|
|
15
|
-
def
|
|
39
|
+
def get_data(self, strategy: GetDataStrategy):
|
|
40
|
+
"""Main method to retrieve caching data using the client-specific
|
|
41
|
+
strategy."""
|
|
42
|
+
cache_key = strategy.get_data_identifier()
|
|
43
|
+
if self._cache_refresh_required(cache_key):
|
|
44
|
+
data = strategy.retrieve_fresh_data()
|
|
45
|
+
self._update_cache(cache_key, data)
|
|
46
|
+
else:
|
|
47
|
+
data = self._read_from_cache(cache_key)
|
|
48
|
+
|
|
49
|
+
return data
|
|
50
|
+
|
|
51
|
+
def _read_from_cache(self, resource_name):
|
|
16
52
|
|
|
17
53
|
platform_helper_config = self.file_provider.load(self._cache_file)
|
|
18
54
|
|
|
19
55
|
return platform_helper_config.get(resource_name).get("versions")
|
|
20
56
|
|
|
21
|
-
def
|
|
57
|
+
def _update_cache(self, resource_name, supported_versions):
|
|
22
58
|
|
|
23
59
|
platform_helper_config = {}
|
|
24
60
|
|
|
@@ -40,7 +76,7 @@ class CacheProvider:
|
|
|
40
76
|
"# [!] This file is autogenerated via the platform-helper. Do not edit.\n",
|
|
41
77
|
)
|
|
42
78
|
|
|
43
|
-
def
|
|
79
|
+
def _cache_refresh_required(self, resource_name) -> bool:
|
|
44
80
|
"""
|
|
45
81
|
Checks if the platform-helper should reach out to AWS to 'refresh' its
|
|
46
82
|
cached values.
|
|
@@ -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(
|
|
@@ -3,9 +3,11 @@ from typing import Callable
|
|
|
3
3
|
import boto3
|
|
4
4
|
|
|
5
5
|
from dbt_platform_helper.platform_exception import PlatformException
|
|
6
|
+
from dbt_platform_helper.providers.aws.opensearch import Opensearch
|
|
7
|
+
from dbt_platform_helper.providers.aws.redis import Redis
|
|
8
|
+
from dbt_platform_helper.providers.cache import Cache
|
|
9
|
+
from dbt_platform_helper.providers.cache import GetAWSVersionStrategy
|
|
6
10
|
from dbt_platform_helper.providers.io import ClickIOProvider
|
|
7
|
-
from dbt_platform_helper.providers.opensearch import OpensearchProvider
|
|
8
|
-
from dbt_platform_helper.providers.redis import RedisProvider
|
|
9
11
|
|
|
10
12
|
|
|
11
13
|
class ConfigValidatorError(PlatformException):
|
|
@@ -15,7 +17,10 @@ class ConfigValidatorError(PlatformException):
|
|
|
15
17
|
class ConfigValidator:
|
|
16
18
|
|
|
17
19
|
def __init__(
|
|
18
|
-
self,
|
|
20
|
+
self,
|
|
21
|
+
validations: Callable[[dict], None] = None,
|
|
22
|
+
io: ClickIOProvider = ClickIOProvider(),
|
|
23
|
+
session: boto3.Session = None,
|
|
19
24
|
):
|
|
20
25
|
self.validations = validations or [
|
|
21
26
|
self.validate_supported_redis_versions,
|
|
@@ -26,13 +31,14 @@ class ConfigValidator:
|
|
|
26
31
|
self.validate_database_migration_input_sources,
|
|
27
32
|
]
|
|
28
33
|
self.io = io
|
|
34
|
+
self.session = session
|
|
29
35
|
|
|
30
36
|
def run_validations(self, config: dict):
|
|
31
37
|
for validation in self.validations:
|
|
32
38
|
validation(config)
|
|
33
39
|
|
|
34
40
|
def _validate_extension_supported_versions(
|
|
35
|
-
self, config, extension_type, version_key
|
|
41
|
+
self, config, aws_provider, extension_type, version_key
|
|
36
42
|
):
|
|
37
43
|
extensions = config.get("extensions", {})
|
|
38
44
|
if not extensions:
|
|
@@ -44,7 +50,10 @@ class ConfigValidator:
|
|
|
44
50
|
if extension.get("type") == extension_type
|
|
45
51
|
]
|
|
46
52
|
|
|
47
|
-
|
|
53
|
+
# In this format so it can be monkey patched initially via mock_get_data fixture
|
|
54
|
+
cache_provider = Cache()
|
|
55
|
+
get_data_strategy = GetAWSVersionStrategy(aws_provider)
|
|
56
|
+
supported_extension_versions = cache_provider.get_data(get_data_strategy)
|
|
48
57
|
extensions_with_invalid_version = []
|
|
49
58
|
|
|
50
59
|
for extension in extensions_for_type:
|
|
@@ -71,24 +80,25 @@ class ConfigValidator:
|
|
|
71
80
|
f"{extension_type} version for environment {version_failure['environment']} is not in the list of supported {extension_type} versions: {supported_extension_versions}. Provided Version: {version_failure['version']}",
|
|
72
81
|
)
|
|
73
82
|
|
|
83
|
+
def _get_client(self, service_name: str):
|
|
84
|
+
if self.session:
|
|
85
|
+
return self.session.client(service_name)
|
|
86
|
+
return boto3.client(service_name)
|
|
87
|
+
|
|
74
88
|
def validate_supported_redis_versions(self, config):
|
|
75
89
|
return self._validate_extension_supported_versions(
|
|
76
90
|
config=config,
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
boto3.client("elasticache")
|
|
81
|
-
).get_supported_redis_versions,
|
|
91
|
+
aws_provider=Redis(self._get_client("elasticache")),
|
|
92
|
+
extension_type="redis", # TODO this is information which can live in the RedisProvider
|
|
93
|
+
version_key="engine", # TODO this is information which can live in the RedisProvider
|
|
82
94
|
)
|
|
83
95
|
|
|
84
96
|
def validate_supported_opensearch_versions(self, config):
|
|
85
97
|
return self._validate_extension_supported_versions(
|
|
86
98
|
config=config,
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
boto3.client("opensearch")
|
|
91
|
-
).get_supported_opensearch_versions,
|
|
99
|
+
aws_provider=Opensearch(self._get_client("opensearch")),
|
|
100
|
+
extension_type="opensearch", # TODO this is information which can live in the OpensearchProvider
|
|
101
|
+
version_key="engine", # TODO this is information which can live in the OpensearchProvider
|
|
92
102
|
)
|
|
93
103
|
|
|
94
104
|
def validate_environment_pipelines(self, config):
|
|
@@ -205,21 +215,14 @@ class ConfigValidator:
|
|
|
205
215
|
f"database_copy 'to' parameter must be a valid environment ({all_envs_string}) but was '{to_env}' in extension '{extension_name}'."
|
|
206
216
|
)
|
|
207
217
|
|
|
218
|
+
# TODO - The from_account and to_account properties are deprecated and will be removed when terraform-platform-modules is merged with platform-tools
|
|
208
219
|
if from_account != to_account:
|
|
209
|
-
if "from_account"
|
|
210
|
-
errors.append(
|
|
211
|
-
f"Environments '{from_env}' and '{to_env}' are in different AWS accounts. The 'from_account' parameter must be present."
|
|
212
|
-
)
|
|
213
|
-
elif section["from_account"] != from_account:
|
|
220
|
+
if "from_account" in section and section["from_account"] != from_account:
|
|
214
221
|
errors.append(
|
|
215
222
|
f"Incorrect value for 'from_account' for environment '{from_env}'"
|
|
216
223
|
)
|
|
217
224
|
|
|
218
|
-
if "to_account"
|
|
219
|
-
errors.append(
|
|
220
|
-
f"Environments '{from_env}' and '{to_env}' are in different AWS accounts. The 'to_account' parameter must be present."
|
|
221
|
-
)
|
|
222
|
-
elif section["to_account"] != to_account:
|
|
225
|
+
if "to_account" in section and section["to_account"] != to_account:
|
|
223
226
|
errors.append(
|
|
224
227
|
f"Incorrect value for 'to_account' for environment '{to_env}'"
|
|
225
228
|
)
|
|
@@ -4,7 +4,7 @@ import time
|
|
|
4
4
|
from botocore.exceptions import ClientError
|
|
5
5
|
|
|
6
6
|
from dbt_platform_helper.constants import CONDUIT_DOCKER_IMAGE_LOCATION
|
|
7
|
-
from dbt_platform_helper.providers.aws import CreateTaskTimeoutException
|
|
7
|
+
from dbt_platform_helper.providers.aws.exceptions import CreateTaskTimeoutException
|
|
8
8
|
from dbt_platform_helper.providers.secrets import Secrets
|
|
9
9
|
from dbt_platform_helper.utils.application import Application
|
|
10
10
|
from dbt_platform_helper.utils.messages import abort_with_error
|
|
@@ -7,11 +7,14 @@ class ClickIOProvider:
|
|
|
7
7
|
def warn(self, message: str):
|
|
8
8
|
click.secho(message, fg="magenta")
|
|
9
9
|
|
|
10
|
+
def debug(self, message: str):
|
|
11
|
+
click.secho(message, fg="green")
|
|
12
|
+
|
|
10
13
|
def error(self, message: str):
|
|
11
14
|
click.secho(f"Error: {message}", fg="red")
|
|
12
15
|
|
|
13
|
-
def info(self, message: str):
|
|
14
|
-
click.secho(message)
|
|
16
|
+
def info(self, message: str, **kwargs):
|
|
17
|
+
click.secho(message, **kwargs)
|
|
15
18
|
|
|
16
19
|
def input(self, message: str) -> str:
|
|
17
20
|
return click.prompt(message)
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import boto3
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class KMSProvider:
|
|
5
|
+
"""A provider class for interacting with the AWS KMS (Key Management
|
|
6
|
+
Service)."""
|
|
7
|
+
|
|
8
|
+
def __init__(self, kms_client: boto3.client):
|
|
9
|
+
self.kms_client = kms_client
|
|
10
|
+
|
|
11
|
+
def describe_key(self, alias_name: str) -> dict:
|
|
12
|
+
"""
|
|
13
|
+
Retrieves metadata about a KMS key using its alias.
|
|
14
|
+
|
|
15
|
+
Args:
|
|
16
|
+
alias_name (str): The alias name of the KMS key.
|
|
17
|
+
|
|
18
|
+
Returns:
|
|
19
|
+
dict: A dictionary containing metadata about the specified KMS key.
|
|
20
|
+
"""
|
|
21
|
+
# The kms client can take an alias name as the KeyId
|
|
22
|
+
return self.kms_client.describe_key(KeyId=alias_name)
|