dbt-platform-helper 12.5.0__py3-none-any.whl → 12.5.1__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 (28) hide show
  1. dbt_platform_helper/COMMANDS.md +1 -3
  2. dbt_platform_helper/commands/copilot.py +4 -2
  3. dbt_platform_helper/commands/environment.py +15 -13
  4. dbt_platform_helper/commands/pipeline.py +8 -170
  5. dbt_platform_helper/constants.py +1 -0
  6. dbt_platform_helper/domain/codebase.py +8 -4
  7. dbt_platform_helper/domain/copilot_environment.py +3 -3
  8. dbt_platform_helper/domain/database_copy.py +9 -7
  9. dbt_platform_helper/domain/maintenance_page.py +44 -20
  10. dbt_platform_helper/domain/pipelines.py +213 -0
  11. dbt_platform_helper/domain/terraform_environment.py +68 -35
  12. dbt_platform_helper/domain/test_platform_terraform_manifest_generator.py +100 -0
  13. dbt_platform_helper/providers/cache.py +1 -2
  14. dbt_platform_helper/providers/config.py +12 -2
  15. dbt_platform_helper/providers/copilot.py +2 -0
  16. dbt_platform_helper/providers/files.py +26 -0
  17. dbt_platform_helper/providers/vpc.py +57 -0
  18. dbt_platform_helper/providers/yaml_file.py +3 -14
  19. dbt_platform_helper/utils/application.py +32 -34
  20. dbt_platform_helper/utils/aws.py +0 -50
  21. dbt_platform_helper/utils/files.py +8 -23
  22. dbt_platform_helper/utils/platform_config.py +0 -7
  23. dbt_platform_helper/utils/versioning.py +12 -0
  24. {dbt_platform_helper-12.5.0.dist-info → dbt_platform_helper-12.5.1.dist-info}/METADATA +1 -1
  25. {dbt_platform_helper-12.5.0.dist-info → dbt_platform_helper-12.5.1.dist-info}/RECORD +28 -24
  26. {dbt_platform_helper-12.5.0.dist-info → dbt_platform_helper-12.5.1.dist-info}/WHEEL +1 -1
  27. {dbt_platform_helper-12.5.0.dist-info → dbt_platform_helper-12.5.1.dist-info}/LICENSE +0 -0
  28. {dbt_platform_helper-12.5.0.dist-info → dbt_platform_helper-12.5.1.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,213 @@
1
+ from collections.abc import Callable
2
+ from os import makedirs
3
+ from pathlib import Path
4
+ from shutil import rmtree
5
+
6
+ from dbt_platform_helper.constants import CODEBASE_PIPELINES_KEY
7
+ from dbt_platform_helper.constants import ENVIRONMENT_PIPELINES_KEY
8
+ from dbt_platform_helper.constants import ENVIRONMENTS_KEY
9
+ from dbt_platform_helper.providers.config import ConfigProvider
10
+ from dbt_platform_helper.providers.files import FileProvider
11
+ from dbt_platform_helper.utils.application import get_application_name
12
+ from dbt_platform_helper.utils.aws import get_account_details
13
+ from dbt_platform_helper.utils.aws import get_public_repository_arn
14
+ from dbt_platform_helper.utils.files import generate_override_files_from_template
15
+ from dbt_platform_helper.utils.template import setup_templates
16
+ from dbt_platform_helper.utils.versioning import (
17
+ get_required_terraform_platform_modules_version,
18
+ )
19
+
20
+
21
+ class Pipelines:
22
+ def __init__(
23
+ self,
24
+ config_provider: ConfigProvider,
25
+ echo: Callable[[str], str],
26
+ abort: Callable[[str], None],
27
+ get_git_remote: Callable[[], str],
28
+ get_codestar_arn: Callable[[str], str],
29
+ ):
30
+ self.config_provider = config_provider
31
+ self.echo = echo
32
+ self.abort = abort
33
+ self.get_git_remote = get_git_remote
34
+ self.get_codestar_arn = get_codestar_arn
35
+
36
+ def generate(self, terraform_platform_modules_version, deploy_branch):
37
+ pipeline_config = self.config_provider.load_and_validate_platform_config()
38
+
39
+ has_codebase_pipelines = CODEBASE_PIPELINES_KEY in pipeline_config
40
+ has_environment_pipelines = ENVIRONMENT_PIPELINES_KEY in pipeline_config
41
+
42
+ if not (has_codebase_pipelines or has_environment_pipelines):
43
+ self.echo("No pipelines defined: nothing to do.", err=True, fg="yellow")
44
+ return
45
+
46
+ platform_config_terraform_modules_default_version = pipeline_config.get(
47
+ "default_versions", {}
48
+ ).get("terraform-platform-modules", "")
49
+
50
+ templates = setup_templates()
51
+ app_name = get_application_name()
52
+
53
+ git_repo = self.get_git_remote()
54
+ if not git_repo:
55
+ self.abort("The current directory is not a git repository")
56
+
57
+ codestar_connection_arn = self.get_codestar_arn(app_name)
58
+ if codestar_connection_arn is None:
59
+ self.abort(f'There is no CodeStar Connection named "{app_name}" to use')
60
+
61
+ base_path = Path(".")
62
+ copilot_pipelines_dir = base_path / f"copilot/pipelines"
63
+
64
+ self._clean_pipeline_config(copilot_pipelines_dir)
65
+
66
+ if has_environment_pipelines:
67
+ environment_pipelines = pipeline_config[ENVIRONMENT_PIPELINES_KEY]
68
+
69
+ for config in environment_pipelines.values():
70
+ aws_account = config.get("account")
71
+ self._generate_terraform_environment_pipeline_manifest(
72
+ pipeline_config["application"],
73
+ aws_account,
74
+ terraform_platform_modules_version,
75
+ platform_config_terraform_modules_default_version,
76
+ deploy_branch,
77
+ )
78
+
79
+ if has_codebase_pipelines:
80
+ account_id, _ = get_account_details()
81
+
82
+ for codebase in pipeline_config[CODEBASE_PIPELINES_KEY]:
83
+ self._generate_codebase_pipeline(
84
+ account_id,
85
+ app_name,
86
+ codestar_connection_arn,
87
+ git_repo,
88
+ codebase,
89
+ base_path,
90
+ copilot_pipelines_dir,
91
+ templates,
92
+ )
93
+
94
+ def _clean_pipeline_config(self, pipelines_dir):
95
+ if pipelines_dir.exists():
96
+ self.echo("Deleting copilot/pipelines directory.")
97
+ rmtree(pipelines_dir)
98
+
99
+ def _generate_codebase_pipeline(
100
+ self,
101
+ account_id,
102
+ app_name,
103
+ codestar_connection_arn,
104
+ git_repo,
105
+ codebase,
106
+ base_path,
107
+ pipelines_dir,
108
+ templates,
109
+ ):
110
+ makedirs(pipelines_dir / codebase["name"] / "overrides", exist_ok=True)
111
+ environments = []
112
+ for pipelines in codebase["pipelines"]:
113
+ environments += pipelines[ENVIRONMENTS_KEY]
114
+
115
+ additional_ecr = codebase.get("additional_ecr_repository", None)
116
+ add_public_perms = additional_ecr and additional_ecr.startswith("public.ecr.aws")
117
+ additional_ecr_arn = get_public_repository_arn(additional_ecr) if add_public_perms else None
118
+
119
+ template_data = {
120
+ "account_id": account_id,
121
+ "app_name": app_name,
122
+ "deploy_repo": git_repo,
123
+ "codebase": codebase,
124
+ ENVIRONMENTS_KEY: environments,
125
+ "codestar_connection_arn": codestar_connection_arn,
126
+ "codestar_connection_id": codestar_connection_arn.split("/")[-1],
127
+ "additional_ecr_arn": additional_ecr_arn,
128
+ }
129
+
130
+ self._create_file_from_template(
131
+ base_path,
132
+ f"{codebase['name']}/manifest.yml",
133
+ pipelines_dir,
134
+ template_data,
135
+ templates,
136
+ "codebase/manifest.yml",
137
+ )
138
+
139
+ overrides_path = Path(__file__).parent.parent.joinpath(
140
+ "templates/pipelines/codebase/overrides"
141
+ )
142
+ generate_override_files_from_template(
143
+ base_path, overrides_path, pipelines_dir / codebase["name"] / "overrides", template_data
144
+ )
145
+
146
+ def _create_file_from_template(
147
+ self, base_path, file_name, pipelines_dir, template_data, templates, template_name=None
148
+ ):
149
+ contents = templates.get_template(
150
+ f"pipelines/{file_name if template_name is None else template_name}"
151
+ ).render(template_data)
152
+ message = FileProvider.mkfile(
153
+ base_path, pipelines_dir / file_name, contents, overwrite=True
154
+ )
155
+ self.echo(message)
156
+
157
+ def _generate_terraform_environment_pipeline_manifest(
158
+ self,
159
+ application,
160
+ aws_account,
161
+ cli_terraform_platform_modules_version,
162
+ platform_config_terraform_modules_default_version,
163
+ deploy_branch,
164
+ ):
165
+ env_pipeline_template = setup_templates().get_template("environment-pipelines/main.tf")
166
+
167
+ terraform_platform_modules_version = get_required_terraform_platform_modules_version(
168
+ cli_terraform_platform_modules_version,
169
+ platform_config_terraform_modules_default_version,
170
+ )
171
+
172
+ contents = env_pipeline_template.render(
173
+ {
174
+ "application": application,
175
+ "aws_account": aws_account,
176
+ "terraform_platform_modules_version": terraform_platform_modules_version,
177
+ "deploy_branch": deploy_branch,
178
+ }
179
+ )
180
+
181
+ dir_path = f"terraform/environment-pipelines/{aws_account}"
182
+ makedirs(dir_path, exist_ok=True)
183
+
184
+ self.echo(FileProvider.mkfile(".", f"{dir_path}/main.tf", contents, overwrite=True))
185
+
186
+ def generate_terraform_codebase_pipeline_manifest(
187
+ self,
188
+ application,
189
+ aws_account,
190
+ cli_terraform_platform_modules_version,
191
+ platform_config_terraform_modules_default_version,
192
+ deploy_branch,
193
+ ):
194
+ env_pipeline_template = setup_templates().get_template("codebase-pipelines/main.tf")
195
+
196
+ terraform_platform_modules_version = get_required_terraform_platform_modules_version(
197
+ cli_terraform_platform_modules_version,
198
+ platform_config_terraform_modules_default_version,
199
+ )
200
+
201
+ contents = env_pipeline_template.render(
202
+ {
203
+ "application": application,
204
+ "aws_account": aws_account,
205
+ "terraform_platform_modules_version": terraform_platform_modules_version,
206
+ "deploy_branch": deploy_branch,
207
+ }
208
+ )
209
+
210
+ dir_path = f"terraform/environment-pipelines/{aws_account}"
211
+ makedirs(dir_path, exist_ok=True)
212
+
213
+ self.echo(FileProvider.mkfile(".", f"{dir_path}/main.tf", contents, overwrite=True))
@@ -1,53 +1,86 @@
1
1
  import click
2
2
 
3
3
  from dbt_platform_helper.constants import DEFAULT_TERRAFORM_PLATFORM_MODULES_VERSION
4
- from dbt_platform_helper.utils.files import mkfile
4
+ from dbt_platform_helper.platform_exception import PlatformException
5
+ from dbt_platform_helper.providers.files import FileProvider
5
6
  from dbt_platform_helper.utils.template import setup_templates
6
7
 
7
8
 
8
- def _generate_terraform_environment_manifests(
9
- application, env, env_config, cli_terraform_platform_modules_version
10
- ):
11
- env_template = setup_templates().get_template("environments/main.tf")
9
+ class PlatformTerraformManifestGenerator:
10
+ def __init__(self, file_provider):
11
+ self.file_provider = file_provider
12
+ self.manifest_template = setup_templates().get_template("environments/main.tf")
12
13
 
13
- terraform_platform_modules_version = _determine_terraform_platform_modules_version(
14
- env_config, cli_terraform_platform_modules_version
15
- )
14
+ def generate_manifest(
15
+ self,
16
+ environment_name: str,
17
+ application_name: str,
18
+ environment_config: dict,
19
+ terraform_platform_modules_version_override: str = None,
20
+ ):
21
+ terraform_platform_modules_version = (
22
+ terraform_platform_modules_version_override
23
+ or environment_config.get("versions", {}).get(
24
+ "terraform-platform-modules", DEFAULT_TERRAFORM_PLATFORM_MODULES_VERSION
25
+ )
26
+ )
27
+
28
+ return self.manifest_template.render(
29
+ {
30
+ "application": application_name,
31
+ "environment": environment_name,
32
+ "config": environment_config,
33
+ "terraform_platform_modules_version": terraform_platform_modules_version,
34
+ }
35
+ )
36
+
37
+ def write_manifest(self, environment_name: str, manifest_content: str):
38
+ return self.file_provider.mkfile(
39
+ ".",
40
+ f"terraform/environments/{environment_name}/main.tf",
41
+ manifest_content,
42
+ overwrite=True,
43
+ )
16
44
 
17
- contents = env_template.render(
18
- {
19
- "application": application,
20
- "environment": env,
21
- "config": env_config,
22
- "terraform_platform_modules_version": terraform_platform_modules_version,
23
- }
24
- )
25
45
 
26
- click.echo(mkfile(".", f"terraform/environments/{env}/main.tf", contents, overwrite=True))
46
+ class TerraformEnvironmentException(PlatformException):
47
+ pass
27
48
 
28
49
 
29
- def _determine_terraform_platform_modules_version(env_conf, cli_terraform_platform_modules_version):
30
- cli_terraform_platform_modules_version = cli_terraform_platform_modules_version
31
- env_conf_terraform_platform_modules_version = env_conf.get("versions", {}).get(
32
- "terraform-platform-modules"
33
- )
34
- version_preference_order = [
35
- cli_terraform_platform_modules_version,
36
- env_conf_terraform_platform_modules_version,
37
- DEFAULT_TERRAFORM_PLATFORM_MODULES_VERSION,
38
- ]
39
- return [version for version in version_preference_order if version][0]
50
+ class EnvironmentNotFoundException(TerraformEnvironmentException):
51
+ pass
40
52
 
41
53
 
42
54
  class TerraformEnvironment:
43
- def __init__(self, config_provider):
55
+ def __init__(
56
+ self,
57
+ config_provider,
58
+ manifest_generator: PlatformTerraformManifestGenerator = None,
59
+ echo_fn=click.echo,
60
+ ):
61
+ self.echo = echo_fn
44
62
  self.config_provider = config_provider
63
+ self.manifest_generator = manifest_generator or PlatformTerraformManifestGenerator(
64
+ FileProvider()
65
+ )
66
+
67
+ def generate(self, environment_name, terraform_platform_modules_version_override=None):
68
+ config = self.config_provider.get_enriched_config()
45
69
 
46
- def generate(self, name, terraform_platform_modules_version):
47
- config = self.config_provider.load_and_validate_platform_config()
48
- enriched_config = self.config_provider.apply_environment_defaults(config)
70
+ if environment_name not in config.get("environments").keys():
71
+ raise EnvironmentNotFoundException(
72
+ f"Error: cannot generate terraform for environment {environment_name}. It does not exist in your configuration"
73
+ )
74
+
75
+ manifest = self.manifest_generator.generate_manifest(
76
+ environment_name=environment_name,
77
+ application_name=config["application"],
78
+ environment_config=config["environments"][environment_name],
79
+ terraform_platform_modules_version_override=terraform_platform_modules_version_override,
80
+ )
49
81
 
50
- env_config = enriched_config["environments"][name]
51
- _generate_terraform_environment_manifests(
52
- config["application"], name, env_config, terraform_platform_modules_version
82
+ self.echo(
83
+ self.manifest_generator.write_manifest(
84
+ environment_name=environment_name, manifest_content=manifest
85
+ )
53
86
  )
@@ -0,0 +1,100 @@
1
+ from unittest.mock import Mock
2
+
3
+ from dbt_platform_helper.constants import DEFAULT_TERRAFORM_PLATFORM_MODULES_VERSION
4
+ from dbt_platform_helper.domain.terraform_environment import (
5
+ PlatformTerraformManifestGenerator,
6
+ )
7
+ from dbt_platform_helper.providers.files import FileProvider
8
+
9
+
10
+ class TestPlatformTerraformManifestGenerator:
11
+
12
+ def test_generator_generates_expected_manifest_content_with_version_override(self):
13
+ test_environment_config = {
14
+ "vpc": "vpc3",
15
+ "accounts": {
16
+ "deploy": {"name": "non-prod-acc", "id": "1122334455"},
17
+ "dns": {"name": "non-prod-dns-acc", "id": "6677889900"},
18
+ },
19
+ "versions": {"terraform-platform-modules": 3},
20
+ }
21
+ expected_header = "# WARNING: This is an autogenerated file, not for manual editing."
22
+ expected_modules = "git::https://github.com/uktrade/terraform-platform-modules.git//extensions?depth=1&ref=123456"
23
+ expected_moved_block = (
24
+ "moved {\n from = module.extensions-tf\n to = module.extensions\n}\n"
25
+ )
26
+
27
+ result = PlatformTerraformManifestGenerator(Mock()).generate_manifest(
28
+ "test", "test-app", test_environment_config, 123456
29
+ )
30
+
31
+ assert expected_header in result
32
+ assert expected_modules in result
33
+ assert expected_moved_block in result
34
+ assert 'environment = "test"' in result
35
+ assert 'application = "test-app"' in result
36
+ assert 'vpc_name = "vpc3"'
37
+
38
+ def test_generator_generates_expected_manifest_content_with_tpm_version_set_in_config(self):
39
+ test_environment_config = {
40
+ "vpc": "vpc3",
41
+ "accounts": {
42
+ "deploy": {"name": "non-prod-acc", "id": "1122334455"},
43
+ "dns": {"name": "non-prod-dns-acc", "id": "6677889900"},
44
+ },
45
+ "versions": {"terraform-platform-modules": 3},
46
+ }
47
+ expected_header = "# WARNING: This is an autogenerated file, not for manual editing."
48
+ expected_modules = "git::https://github.com/uktrade/terraform-platform-modules.git//extensions?depth=1&ref=3"
49
+ expected_moved_block = (
50
+ "moved {\n from = module.extensions-tf\n to = module.extensions\n}\n"
51
+ )
52
+
53
+ result = PlatformTerraformManifestGenerator(Mock()).generate_manifest(
54
+ "test", "test-app", test_environment_config
55
+ )
56
+
57
+ assert expected_header in result
58
+ assert expected_modules in result
59
+ assert expected_moved_block in result
60
+ assert 'environment = "test"' in result
61
+ assert 'application = "test-app"' in result
62
+ assert 'vpc_name = "vpc3"'
63
+
64
+ def test_generator_generates_expected_manifest_content_with_default_version(self):
65
+ test_environment_config = {
66
+ "vpc": "vpc3",
67
+ "accounts": {
68
+ "deploy": {"name": "non-prod-acc", "id": "1122334455"},
69
+ "dns": {"name": "non-prod-dns-acc", "id": "6677889900"},
70
+ },
71
+ }
72
+ expected_header = "# WARNING: This is an autogenerated file, not for manual editing."
73
+ expected_modules = f"git::https://github.com/uktrade/terraform-platform-modules.git//extensions?depth=1&ref={DEFAULT_TERRAFORM_PLATFORM_MODULES_VERSION}"
74
+ expected_moved_block = (
75
+ "moved {\n from = module.extensions-tf\n to = module.extensions\n}\n"
76
+ )
77
+
78
+ result = PlatformTerraformManifestGenerator(Mock()).generate_manifest(
79
+ "test", "test-app", test_environment_config
80
+ )
81
+
82
+ assert expected_header in result
83
+ assert expected_modules in result
84
+ assert expected_moved_block in result
85
+ assert 'environment = "test"' in result
86
+ assert 'application = "test-app"' in result
87
+ assert 'vpc_name = "vpc3"'
88
+
89
+ def test_generator_write_manifest_makes_the_expected_manifest_file(self):
90
+ mock_file_provider = Mock(spec=FileProvider)
91
+ PlatformTerraformManifestGenerator(mock_file_provider).write_manifest(
92
+ "test-environment", "test-manifest-content"
93
+ )
94
+
95
+ mock_file_provider.mkfile.assert_called_once_with(
96
+ ".",
97
+ f"terraform/environments/test-environment/main.tf",
98
+ "test-manifest-content",
99
+ overwrite=True,
100
+ )
@@ -1,14 +1,13 @@
1
1
  import os
2
2
  from datetime import datetime
3
3
 
4
- from dbt_platform_helper.providers.yaml_file import FileProvider
5
4
  from dbt_platform_helper.providers.yaml_file import YamlFileProvider
6
5
 
7
6
 
8
7
  class CacheProvider:
9
8
  def __init__(
10
9
  self,
11
- file_provider: FileProvider = None,
10
+ file_provider: YamlFileProvider = None,
12
11
  ):
13
12
  self._cache_file = ".platform-helper-config-cache.yml"
14
13
  self.file_provider = file_provider or YamlFileProvider
@@ -7,7 +7,7 @@ from schema import SchemaError
7
7
  from dbt_platform_helper.constants import PLATFORM_CONFIG_FILE
8
8
  from dbt_platform_helper.domain.config_validator import ConfigValidator
9
9
  from dbt_platform_helper.providers.platform_config_schema import PlatformConfigSchema
10
- from dbt_platform_helper.providers.yaml_file import FileProvider
10
+ from dbt_platform_helper.providers.yaml_file import FileNotFoundException
11
11
  from dbt_platform_helper.providers.yaml_file import FileProviderException
12
12
  from dbt_platform_helper.providers.yaml_file import YamlFileProvider
13
13
  from dbt_platform_helper.utils.messages import abort_with_error
@@ -17,7 +17,7 @@ class ConfigProvider:
17
17
  def __init__(
18
18
  self,
19
19
  config_validator: ConfigValidator,
20
- file_provider: FileProvider = None,
20
+ file_provider: YamlFileProvider = None,
21
21
  echo=click.secho,
22
22
  ):
23
23
  self.config = {}
@@ -25,16 +25,26 @@ class ConfigProvider:
25
25
  self.echo = echo
26
26
  self.file_provider = file_provider or YamlFileProvider
27
27
 
28
+ # TODO refactor so that apply_environment_defaults isn't set, discarded and set again
29
+ def get_enriched_config(self):
30
+ return self.apply_environment_defaults(self.load_and_validate_platform_config())
31
+
28
32
  def validate_platform_config(self):
29
33
  PlatformConfigSchema.schema().validate(self.config)
30
34
 
31
35
  # TODO= logically this isn't validation but loading + parsing, to move.
36
+ # also, we apply defaults but discard that data. Should we just apply
37
+ # defaults to config returned by load_and_validate
32
38
  enriched_config = ConfigProvider.apply_environment_defaults(self.config)
33
39
  self.validator.run_validations(enriched_config)
34
40
 
35
41
  def load_and_validate_platform_config(self, path=PLATFORM_CONFIG_FILE):
36
42
  try:
37
43
  self.config = self.file_provider.load(path)
44
+ except FileNotFoundException as e:
45
+ abort_with_error(
46
+ f"{e} Please check it exists and you are in the root directory of your deployment project."
47
+ )
38
48
  except FileProviderException as e:
39
49
  abort_with_error(f"Error loading configuration from {path}: {e}")
40
50
 
@@ -64,6 +64,7 @@ def create_addon_client_task(
64
64
  f"{execution_role}"
65
65
  f"--image {CONDUIT_DOCKER_IMAGE_LOCATION}:{addon_type} "
66
66
  f"--secrets CONNECTION_SECRET={_get_secrets_provider(application, env).get_connection_secret_arn(secret_name)} "
67
+ "--cpu 2048 --memory 4096 "
67
68
  "--platform-os linux "
68
69
  "--platform-arch arm64",
69
70
  shell=True,
@@ -98,6 +99,7 @@ def create_postgres_admin_task(
98
99
  f"--task-group-name {task_name} "
99
100
  f"--image {CONDUIT_DOCKER_IMAGE_LOCATION}:{addon_type} "
100
101
  f"--env-vars CONNECTION_SECRET='{connection_string}' "
102
+ "--cpu 2048 --memory 4096 "
101
103
  "--platform-os linux "
102
104
  "--platform-arch arm64",
103
105
  shell=True,
@@ -0,0 +1,26 @@
1
+ from os import makedirs
2
+ from pathlib import Path
3
+
4
+
5
+ class FileProvider:
6
+
7
+ def load(path: str) -> str:
8
+ pass
9
+
10
+ @staticmethod
11
+ def mkfile(base_path: str, file_path: str, contents, overwrite=False) -> str:
12
+ file_path = Path(file_path)
13
+ file = Path(base_path).joinpath(file_path)
14
+ file_exists = file.exists()
15
+
16
+ if not file_path.parent.exists():
17
+ makedirs(file_path.parent)
18
+
19
+ if file_exists and not overwrite:
20
+ return f"File {file_path} exists; doing nothing"
21
+
22
+ action = "overwritten" if file_exists and overwrite else "created"
23
+
24
+ file.write_text(contents)
25
+
26
+ return f"File {file_path} {action}"
@@ -0,0 +1,57 @@
1
+ from dataclasses import dataclass
2
+
3
+ from dbt_platform_helper.providers.aws import AWSException
4
+
5
+
6
+ @dataclass
7
+ class Vpc:
8
+ subnets: list[str]
9
+ security_groups: list[str]
10
+
11
+
12
+ class VpcProvider:
13
+ def __init__(self, session):
14
+ self.ec2_client = session.client("ec2")
15
+ self.ec2_resource = session.resource("ec2")
16
+
17
+ def get_vpc_info_by_name(self, app: str, env: str, vpc_name: str) -> Vpc:
18
+ vpc_response = self.ec2_client.describe_vpcs(
19
+ Filters=[{"Name": "tag:Name", "Values": [vpc_name]}]
20
+ )
21
+
22
+ matching_vpcs = vpc_response.get("Vpcs", [])
23
+
24
+ if not matching_vpcs:
25
+ raise AWSException(f"VPC not found for name '{vpc_name}'")
26
+
27
+ vpc_id = vpc_response["Vpcs"][0].get("VpcId")
28
+
29
+ if not vpc_id:
30
+ raise AWSException(f"VPC id not present in vpc '{vpc_name}'")
31
+
32
+ vpc = self.ec2_resource.Vpc(vpc_id)
33
+
34
+ route_tables = self.ec2_client.describe_route_tables(
35
+ Filters=[{"Name": "vpc-id", "Values": [vpc_id]}]
36
+ )["RouteTables"]
37
+
38
+ subnets = []
39
+ for route_table in route_tables:
40
+ private_routes = [route for route in route_table["Routes"] if "NatGatewayId" in route]
41
+ if not private_routes:
42
+ continue
43
+ for association in route_table["Associations"]:
44
+ if "SubnetId" in association:
45
+ subnet_id = association["SubnetId"]
46
+ subnets.append(subnet_id)
47
+
48
+ if not subnets:
49
+ raise AWSException(f"No private subnets found in vpc '{vpc_name}'")
50
+
51
+ tag_value = {"Key": "Name", "Value": f"copilot-{app}-{env}-env"}
52
+ sec_groups = [sg.id for sg in vpc.security_groups.all() if sg.tags and tag_value in sg.tags]
53
+
54
+ if not sec_groups:
55
+ raise AWSException(f"No matching security groups found in vpc '{vpc_name}'")
56
+
57
+ return Vpc(subnets, sec_groups)
@@ -1,5 +1,3 @@
1
- from abc import ABC
2
- from abc import abstractmethod
3
1
  from pathlib import Path
4
2
 
5
3
  import yaml
@@ -16,7 +14,7 @@ class YamlFileProviderException(FileProviderException):
16
14
  pass
17
15
 
18
16
 
19
- class FileNotFoundException(YamlFileProviderException):
17
+ class FileNotFoundException(FileProviderException):
20
18
  pass
21
19
 
22
20
 
@@ -28,13 +26,7 @@ class DuplicateKeysException(YamlFileProviderException):
28
26
  pass
29
27
 
30
28
 
31
- class FileProvider(ABC):
32
- @abstractmethod
33
- def load(path: str) -> dict:
34
- raise NotImplementedError("Implement this in the subclass")
35
-
36
-
37
- class YamlFileProvider(FileProvider):
29
+ class YamlFileProvider:
38
30
  def load(path: str) -> dict:
39
31
  """
40
32
  Raises:
@@ -43,10 +35,7 @@ class YamlFileProvider(FileProvider):
43
35
  DuplicateKeysException: yaml contains duplicate keys
44
36
  """
45
37
  if not Path(path).exists():
46
- # TODO this error message is domain specific and should not mention deployment directory project here
47
- raise FileNotFoundException(
48
- f"`{path}` is missing. Please check it exists and you are in the root directory of your deployment project."
49
- )
38
+ raise FileNotFoundException(f"`{path}` is missing.")
50
39
  try:
51
40
  yaml_content = yaml.safe_load(Path(path).read_text())
52
41
  except ParserError: