dbt-platform-helper 12.4.1__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.
- dbt_platform_helper/COMMANDS.md +1 -6
- dbt_platform_helper/commands/config.py +2 -2
- dbt_platform_helper/commands/copilot.py +51 -30
- dbt_platform_helper/commands/environment.py +25 -185
- dbt_platform_helper/commands/pipeline.py +10 -173
- dbt_platform_helper/constants.py +10 -0
- dbt_platform_helper/domain/codebase.py +8 -4
- dbt_platform_helper/domain/config_validator.py +242 -0
- dbt_platform_helper/domain/copilot_environment.py +204 -0
- dbt_platform_helper/domain/database_copy.py +16 -12
- dbt_platform_helper/domain/maintenance_page.py +44 -20
- dbt_platform_helper/domain/pipelines.py +213 -0
- dbt_platform_helper/domain/terraform_environment.py +86 -0
- dbt_platform_helper/domain/test_platform_terraform_manifest_generator.py +100 -0
- dbt_platform_helper/jinja2_tags.py +1 -1
- dbt_platform_helper/providers/cache.py +14 -21
- dbt_platform_helper/providers/cloudformation.py +0 -1
- dbt_platform_helper/providers/config.py +100 -0
- dbt_platform_helper/providers/copilot.py +2 -0
- dbt_platform_helper/providers/files.py +26 -0
- dbt_platform_helper/providers/opensearch.py +36 -0
- dbt_platform_helper/providers/platform_config_schema.py +589 -527
- dbt_platform_helper/providers/redis.py +34 -0
- dbt_platform_helper/providers/vpc.py +57 -0
- dbt_platform_helper/providers/yaml_file.py +72 -0
- dbt_platform_helper/templates/addons/svc/s3-cross-account-policy.yml +67 -0
- dbt_platform_helper/utils/application.py +32 -34
- dbt_platform_helper/utils/aws.py +1 -107
- dbt_platform_helper/utils/files.py +8 -59
- dbt_platform_helper/utils/platform_config.py +0 -7
- dbt_platform_helper/utils/template.py +10 -0
- dbt_platform_helper/utils/validation.py +5 -327
- dbt_platform_helper/utils/versioning.py +12 -0
- {dbt_platform_helper-12.4.1.dist-info → dbt_platform_helper-12.5.1.dist-info}/METADATA +2 -2
- {dbt_platform_helper-12.4.1.dist-info → dbt_platform_helper-12.5.1.dist-info}/RECORD +38 -26
- {dbt_platform_helper-12.4.1.dist-info → dbt_platform_helper-12.5.1.dist-info}/WHEEL +1 -1
- {dbt_platform_helper-12.4.1.dist-info → dbt_platform_helper-12.5.1.dist-info}/LICENSE +0 -0
- {dbt_platform_helper-12.4.1.dist-info → dbt_platform_helper-12.5.1.dist-info}/entry_points.txt +0 -0
|
@@ -3,38 +3,62 @@ import random
|
|
|
3
3
|
import re
|
|
4
4
|
import string
|
|
5
5
|
from pathlib import Path
|
|
6
|
+
from typing import Callable
|
|
6
7
|
from typing import List
|
|
7
8
|
from typing import Union
|
|
8
9
|
|
|
9
10
|
import boto3
|
|
10
11
|
import click
|
|
11
12
|
|
|
13
|
+
from dbt_platform_helper.platform_exception import PlatformException
|
|
12
14
|
from dbt_platform_helper.providers.load_balancers import ListenerNotFoundException
|
|
13
15
|
from dbt_platform_helper.providers.load_balancers import ListenerRuleNotFoundException
|
|
14
16
|
from dbt_platform_helper.providers.load_balancers import LoadBalancerNotFoundException
|
|
15
17
|
from dbt_platform_helper.providers.load_balancers import find_https_listener
|
|
18
|
+
from dbt_platform_helper.utils.application import Application
|
|
16
19
|
from dbt_platform_helper.utils.application import Environment
|
|
17
20
|
from dbt_platform_helper.utils.application import Service
|
|
18
21
|
from dbt_platform_helper.utils.application import load_application
|
|
19
22
|
|
|
20
23
|
|
|
21
|
-
class
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
24
|
+
class MaintenancePageException(PlatformException):
|
|
25
|
+
pass
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class LoadBalancedWebServiceNotFoundException(MaintenancePageException):
|
|
29
|
+
pass
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class MaintenancePage:
|
|
33
|
+
def __init__(
|
|
34
|
+
self,
|
|
35
|
+
user_prompt_callback: Callable[[str], bool] = click.confirm,
|
|
36
|
+
echo: Callable[[str], str] = click.secho,
|
|
37
|
+
):
|
|
38
|
+
self.user_prompt_callback = user_prompt_callback
|
|
39
|
+
self.echo = echo
|
|
25
40
|
|
|
41
|
+
def _get_deployed_load_balanced_web_services(self, app: Application, svc: str):
|
|
26
42
|
if "*" in svc:
|
|
27
|
-
services = [
|
|
28
|
-
s for s in application.services.values() if s.kind == "Load Balanced Web Service"
|
|
29
|
-
]
|
|
43
|
+
services = [s for s in app.services.values() if s.kind == "Load Balanced Web Service"]
|
|
30
44
|
else:
|
|
31
|
-
all_services = [get_app_service(app, s) for s in list(svc)]
|
|
45
|
+
all_services = [get_app_service(app.name, s) for s in list(svc)]
|
|
32
46
|
services = [s for s in all_services if s.kind == "Load Balanced Web Service"]
|
|
33
|
-
|
|
34
47
|
if not services:
|
|
35
|
-
|
|
48
|
+
raise LoadBalancedWebServiceNotFoundException
|
|
49
|
+
return services
|
|
50
|
+
|
|
51
|
+
def activate(self, app, env, svc, template, vpc):
|
|
52
|
+
try:
|
|
53
|
+
services = self._get_deployed_load_balanced_web_services(load_application(app), svc)
|
|
54
|
+
except LoadBalancedWebServiceNotFoundException:
|
|
55
|
+
# TODO DBTP-1643 - this bit of logic does not depend on env, so env shouldn't really be in the exception
|
|
56
|
+
# message
|
|
57
|
+
# Exception should be propagated to command and caught there.
|
|
58
|
+
self.echo(f"No services deployed yet to {app} environment {env}", fg="red")
|
|
36
59
|
raise click.Abort
|
|
37
60
|
|
|
61
|
+
application_environment = get_app_environment(app, env)
|
|
38
62
|
try:
|
|
39
63
|
https_listener = find_https_listener(application_environment.session, app, env)
|
|
40
64
|
current_maintenance_page = get_maintenance_page(
|
|
@@ -42,7 +66,7 @@ class MaintenancePageProvider:
|
|
|
42
66
|
)
|
|
43
67
|
remove_current_maintenance_page = False
|
|
44
68
|
if current_maintenance_page:
|
|
45
|
-
remove_current_maintenance_page =
|
|
69
|
+
remove_current_maintenance_page = self.user_prompt_callback(
|
|
46
70
|
f"There is currently a '{current_maintenance_page}' maintenance page for the {env} "
|
|
47
71
|
f"environment in {app}.\nWould you like to replace it with a '{template}' "
|
|
48
72
|
f"maintenance page?"
|
|
@@ -50,7 +74,7 @@ class MaintenancePageProvider:
|
|
|
50
74
|
if not remove_current_maintenance_page:
|
|
51
75
|
raise click.Abort
|
|
52
76
|
|
|
53
|
-
if remove_current_maintenance_page or
|
|
77
|
+
if remove_current_maintenance_page or self.user_prompt_callback(
|
|
54
78
|
f"You are about to enable the '{template}' maintenance page for the {env} "
|
|
55
79
|
f"environment in {app}.\nWould you like to continue?"
|
|
56
80
|
):
|
|
@@ -68,7 +92,7 @@ class MaintenancePageProvider:
|
|
|
68
92
|
allowed_ips,
|
|
69
93
|
template,
|
|
70
94
|
)
|
|
71
|
-
|
|
95
|
+
self.echo(
|
|
72
96
|
f"Maintenance page '{template}' added for environment {env} in application {app}",
|
|
73
97
|
fg="green",
|
|
74
98
|
)
|
|
@@ -76,13 +100,13 @@ class MaintenancePageProvider:
|
|
|
76
100
|
raise click.Abort
|
|
77
101
|
|
|
78
102
|
except LoadBalancerNotFoundException:
|
|
79
|
-
|
|
103
|
+
self.echo(
|
|
80
104
|
f"No load balancer found for environment {env} in the application {app}.", fg="red"
|
|
81
105
|
)
|
|
82
106
|
raise click.Abort
|
|
83
107
|
|
|
84
108
|
except ListenerNotFoundException:
|
|
85
|
-
|
|
109
|
+
self.echo(
|
|
86
110
|
f"No HTTPS listener found for environment {env} in the application {app}.", fg="red"
|
|
87
111
|
)
|
|
88
112
|
raise click.Abort
|
|
@@ -96,28 +120,28 @@ class MaintenancePageProvider:
|
|
|
96
120
|
application_environment.session, https_listener
|
|
97
121
|
)
|
|
98
122
|
if not current_maintenance_page:
|
|
99
|
-
|
|
123
|
+
self.echo("There is no current maintenance page to remove", fg="red")
|
|
100
124
|
raise click.Abort
|
|
101
125
|
|
|
102
|
-
if not
|
|
126
|
+
if not self.user_prompt_callback(
|
|
103
127
|
f"There is currently a '{current_maintenance_page}' maintenance page, "
|
|
104
128
|
f"would you like to remove it?"
|
|
105
129
|
):
|
|
106
130
|
raise click.Abort
|
|
107
131
|
|
|
108
132
|
remove_maintenance_page(application_environment.session, https_listener)
|
|
109
|
-
|
|
133
|
+
self.echo(
|
|
110
134
|
f"Maintenance page removed from environment {env} in application {app}", fg="green"
|
|
111
135
|
)
|
|
112
136
|
|
|
113
137
|
except LoadBalancerNotFoundException:
|
|
114
|
-
|
|
138
|
+
self.echo(
|
|
115
139
|
f"No load balancer found for environment {env} in the application {app}.", fg="red"
|
|
116
140
|
)
|
|
117
141
|
raise click.Abort
|
|
118
142
|
|
|
119
143
|
except ListenerNotFoundException:
|
|
120
|
-
|
|
144
|
+
self.echo(
|
|
121
145
|
f"No HTTPS listener found for environment {env} in the application {app}.", fg="red"
|
|
122
146
|
)
|
|
123
147
|
raise click.Abort
|
|
@@ -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))
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import click
|
|
2
|
+
|
|
3
|
+
from dbt_platform_helper.constants import DEFAULT_TERRAFORM_PLATFORM_MODULES_VERSION
|
|
4
|
+
from dbt_platform_helper.platform_exception import PlatformException
|
|
5
|
+
from dbt_platform_helper.providers.files import FileProvider
|
|
6
|
+
from dbt_platform_helper.utils.template import setup_templates
|
|
7
|
+
|
|
8
|
+
|
|
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")
|
|
13
|
+
|
|
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
|
+
)
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
class TerraformEnvironmentException(PlatformException):
|
|
47
|
+
pass
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
class EnvironmentNotFoundException(TerraformEnvironmentException):
|
|
51
|
+
pass
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
class TerraformEnvironment:
|
|
55
|
+
def __init__(
|
|
56
|
+
self,
|
|
57
|
+
config_provider,
|
|
58
|
+
manifest_generator: PlatformTerraformManifestGenerator = None,
|
|
59
|
+
echo_fn=click.echo,
|
|
60
|
+
):
|
|
61
|
+
self.echo = echo_fn
|
|
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()
|
|
69
|
+
|
|
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
|
+
)
|
|
81
|
+
|
|
82
|
+
self.echo(
|
|
83
|
+
self.manifest_generator.write_manifest(
|
|
84
|
+
environment_name=environment_name, manifest_content=manifest
|
|
85
|
+
)
|
|
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,17 +1,20 @@
|
|
|
1
1
|
import os
|
|
2
2
|
from datetime import datetime
|
|
3
|
-
from pathlib import Path
|
|
4
3
|
|
|
5
|
-
import
|
|
4
|
+
from dbt_platform_helper.providers.yaml_file import YamlFileProvider
|
|
6
5
|
|
|
7
6
|
|
|
8
7
|
class CacheProvider:
|
|
9
|
-
def __init__(
|
|
8
|
+
def __init__(
|
|
9
|
+
self,
|
|
10
|
+
file_provider: YamlFileProvider = None,
|
|
11
|
+
):
|
|
10
12
|
self._cache_file = ".platform-helper-config-cache.yml"
|
|
13
|
+
self.file_provider = file_provider or YamlFileProvider
|
|
11
14
|
|
|
12
15
|
def read_supported_versions_from_cache(self, resource_name):
|
|
13
16
|
|
|
14
|
-
platform_helper_config = self.
|
|
17
|
+
platform_helper_config = self.file_provider.load(self._cache_file)
|
|
15
18
|
|
|
16
19
|
return platform_helper_config.get(resource_name).get("versions")
|
|
17
20
|
|
|
@@ -20,7 +23,7 @@ class CacheProvider:
|
|
|
20
23
|
platform_helper_config = {}
|
|
21
24
|
|
|
22
25
|
if self.__cache_exists():
|
|
23
|
-
platform_helper_config = self.
|
|
26
|
+
platform_helper_config = self.file_provider.load(self._cache_file)
|
|
24
27
|
|
|
25
28
|
cache_dict = {
|
|
26
29
|
resource_name: {
|
|
@@ -31,7 +34,11 @@ class CacheProvider:
|
|
|
31
34
|
|
|
32
35
|
platform_helper_config.update(cache_dict)
|
|
33
36
|
|
|
34
|
-
self.
|
|
37
|
+
self.file_provider.write(
|
|
38
|
+
self._cache_file,
|
|
39
|
+
platform_helper_config,
|
|
40
|
+
"# [!] This file is autogenerated via the platform-helper. Do not edit.\n",
|
|
41
|
+
)
|
|
35
42
|
|
|
36
43
|
def cache_refresh_required(self, resource_name) -> bool:
|
|
37
44
|
"""
|
|
@@ -47,7 +54,7 @@ class CacheProvider:
|
|
|
47
54
|
if not self.__cache_exists():
|
|
48
55
|
return True
|
|
49
56
|
|
|
50
|
-
platform_helper_config = self.
|
|
57
|
+
platform_helper_config = self.file_provider.load(self._cache_file)
|
|
51
58
|
|
|
52
59
|
if platform_helper_config.get(resource_name):
|
|
53
60
|
return self.__check_if_cached_datetime_is_greater_than_interval(
|
|
@@ -65,19 +72,5 @@ class CacheProvider:
|
|
|
65
72
|
|
|
66
73
|
return delta.days > interval_in_days
|
|
67
74
|
|
|
68
|
-
# TODO - same applies here as below
|
|
69
|
-
@staticmethod
|
|
70
|
-
def __read_file_as_yaml(file_name):
|
|
71
|
-
|
|
72
|
-
return yaml.safe_load(Path(file_name).read_text())
|
|
73
|
-
|
|
74
|
-
# TODO - temp fix to the unit test coverage issue, plan is to seperate out any yaml interaction methods into a seperate 'yaml' provider
|
|
75
|
-
# should be done under a different sub-task which will need to loop back to this provider as part of that work to use the yaml provider instead
|
|
76
|
-
def __write_cache(self, contents):
|
|
77
|
-
|
|
78
|
-
with open(self._cache_file, "w") as file:
|
|
79
|
-
file.write("# [!] This file is autogenerated via the platform-helper. Do not edit.\n")
|
|
80
|
-
yaml.dump(contents, file)
|
|
81
|
-
|
|
82
75
|
def __cache_exists(self):
|
|
83
76
|
return os.path.exists(self._cache_file)
|
|
@@ -93,7 +93,6 @@ class CloudFormation:
|
|
|
93
93
|
)
|
|
94
94
|
|
|
95
95
|
params = []
|
|
96
|
-
# TODO Currently not covered by tests - see https://uktrade.atlassian.net/browse/DBTP-1582
|
|
97
96
|
if "Parameters" in template_yml:
|
|
98
97
|
for param in template_yml["Parameters"]:
|
|
99
98
|
params.append({"ParameterKey": param, "UsePreviousValue": True})
|