dbt-platform-helper 13.1.1__py3-none-any.whl → 13.2.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 +3 -5
- dbt_platform_helper/commands/codebase.py +2 -4
- dbt_platform_helper/commands/conduit.py +2 -4
- dbt_platform_helper/commands/config.py +19 -17
- dbt_platform_helper/commands/copilot.py +13 -390
- dbt_platform_helper/commands/environment.py +6 -6
- dbt_platform_helper/commands/generate.py +2 -3
- dbt_platform_helper/commands/notify.py +2 -4
- dbt_platform_helper/commands/pipeline.py +2 -4
- dbt_platform_helper/commands/secrets.py +2 -4
- dbt_platform_helper/commands/version.py +2 -2
- dbt_platform_helper/domain/codebase.py +14 -11
- dbt_platform_helper/domain/copilot.py +397 -0
- dbt_platform_helper/domain/copilot_environment.py +6 -6
- dbt_platform_helper/domain/maintenance_page.py +227 -431
- dbt_platform_helper/domain/pipelines.py +1 -1
- dbt_platform_helper/domain/terraform_environment.py +1 -1
- dbt_platform_helper/domain/versioning.py +157 -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/cache.py +40 -4
- dbt_platform_helper/providers/config_validator.py +15 -14
- dbt_platform_helper/providers/copilot.py +1 -1
- dbt_platform_helper/providers/io.py +17 -0
- dbt_platform_helper/providers/kms.py +22 -0
- dbt_platform_helper/providers/load_balancers.py +269 -43
- dbt_platform_helper/providers/semantic_version.py +33 -10
- dbt_platform_helper/providers/version.py +42 -0
- dbt_platform_helper/providers/yaml_file.py +0 -1
- dbt_platform_helper/utils/application.py +14 -0
- dbt_platform_helper/utils/aws.py +27 -4
- dbt_platform_helper/utils/tool_versioning.py +96 -0
- {dbt_platform_helper-13.1.1.dist-info → dbt_platform_helper-13.2.0.dist-info}/METADATA +3 -4
- {dbt_platform_helper-13.1.1.dist-info → dbt_platform_helper-13.2.0.dist-info}/RECORD +39 -35
- dbt_platform_helper/providers/opensearch.py +0 -36
- dbt_platform_helper/providers/redis.py +0 -34
- dbt_platform_helper/utils/versioning.py +0 -238
- /dbt_platform_helper/providers/{aws.py → aws/exceptions.py} +0 -0
- {dbt_platform_helper-13.1.1.dist-info → dbt_platform_helper-13.2.0.dist-info}/LICENSE +0 -0
- {dbt_platform_helper-13.1.1.dist-info → dbt_platform_helper-13.2.0.dist-info}/WHEEL +0 -0
- {dbt_platform_helper-13.1.1.dist-info → dbt_platform_helper-13.2.0.dist-info}/entry_points.txt +0 -0
|
@@ -2,16 +2,14 @@ import click
|
|
|
2
2
|
from slack_sdk import WebClient
|
|
3
3
|
from slack_sdk.models import blocks
|
|
4
4
|
|
|
5
|
+
from dbt_platform_helper.domain.versioning import PlatformHelperVersioning
|
|
5
6
|
from dbt_platform_helper.utils.arn_parser import ARN
|
|
6
7
|
from dbt_platform_helper.utils.click import ClickDocOptGroup
|
|
7
|
-
from dbt_platform_helper.utils.versioning import (
|
|
8
|
-
check_platform_helper_version_needs_update,
|
|
9
|
-
)
|
|
10
8
|
|
|
11
9
|
|
|
12
10
|
@click.group(cls=ClickDocOptGroup, help="Send Slack notifications")
|
|
13
11
|
def notify():
|
|
14
|
-
|
|
12
|
+
PlatformHelperVersioning().check_if_needs_update()
|
|
15
13
|
|
|
16
14
|
|
|
17
15
|
@notify.command(
|
|
@@ -3,6 +3,7 @@ import click
|
|
|
3
3
|
|
|
4
4
|
from dbt_platform_helper.constants import DEFAULT_TERRAFORM_PLATFORM_MODULES_VERSION
|
|
5
5
|
from dbt_platform_helper.domain.pipelines import Pipelines
|
|
6
|
+
from dbt_platform_helper.domain.versioning import PlatformHelperVersioning
|
|
6
7
|
from dbt_platform_helper.providers.config import ConfigProvider
|
|
7
8
|
from dbt_platform_helper.providers.config_validator import ConfigValidator
|
|
8
9
|
from dbt_platform_helper.providers.ecr import ECRProvider
|
|
@@ -11,15 +12,12 @@ from dbt_platform_helper.providers.terraform_manifest import TerraformManifestPr
|
|
|
11
12
|
from dbt_platform_helper.utils.aws import get_codestar_connection_arn
|
|
12
13
|
from dbt_platform_helper.utils.click import ClickDocOptGroup
|
|
13
14
|
from dbt_platform_helper.utils.git import git_remote
|
|
14
|
-
from dbt_platform_helper.utils.versioning import (
|
|
15
|
-
check_platform_helper_version_needs_update,
|
|
16
|
-
)
|
|
17
15
|
|
|
18
16
|
|
|
19
17
|
@click.group(chain=True, cls=ClickDocOptGroup)
|
|
20
18
|
def pipeline():
|
|
21
19
|
"""Pipeline commands."""
|
|
22
|
-
|
|
20
|
+
PlatformHelperVersioning().check_if_needs_update()
|
|
23
21
|
|
|
24
22
|
|
|
25
23
|
@pipeline.command()
|
|
@@ -6,15 +6,13 @@ import click
|
|
|
6
6
|
from botocore.exceptions import ClientError
|
|
7
7
|
from cloudfoundry_client.client import CloudFoundryClient
|
|
8
8
|
|
|
9
|
+
from dbt_platform_helper.domain.versioning import PlatformHelperVersioning
|
|
9
10
|
from dbt_platform_helper.utils.application import get_application_name
|
|
10
11
|
from dbt_platform_helper.utils.aws import SSM_BASE_PATH
|
|
11
12
|
from dbt_platform_helper.utils.aws import get_aws_session_or_abort
|
|
12
13
|
from dbt_platform_helper.utils.aws import get_ssm_secrets
|
|
13
14
|
from dbt_platform_helper.utils.aws import set_ssm_param
|
|
14
15
|
from dbt_platform_helper.utils.click import ClickDocOptGroup
|
|
15
|
-
from dbt_platform_helper.utils.versioning import (
|
|
16
|
-
check_platform_helper_version_needs_update,
|
|
17
|
-
)
|
|
18
16
|
|
|
19
17
|
|
|
20
18
|
def secret_should_be_skipped(secret_name):
|
|
@@ -42,7 +40,7 @@ def get_paas_env_vars(client: CloudFoundryClient, paas: str) -> dict:
|
|
|
42
40
|
|
|
43
41
|
@click.group(chain=True, cls=ClickDocOptGroup)
|
|
44
42
|
def secrets():
|
|
45
|
-
|
|
43
|
+
PlatformHelperVersioning().check_if_needs_update()
|
|
46
44
|
|
|
47
45
|
|
|
48
46
|
@secrets.command()
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import click
|
|
2
2
|
|
|
3
|
+
from dbt_platform_helper.domain.versioning import PlatformHelperVersioning
|
|
3
4
|
from dbt_platform_helper.platform_exception import PlatformException
|
|
4
5
|
from dbt_platform_helper.providers.io import ClickIOProvider
|
|
5
6
|
from dbt_platform_helper.utils.click import ClickDocOptGroup
|
|
6
|
-
from dbt_platform_helper.utils.versioning import RequiredVersion
|
|
7
7
|
|
|
8
8
|
|
|
9
9
|
@click.group(chain=True, cls=ClickDocOptGroup)
|
|
@@ -32,6 +32,6 @@ def get_platform_helper_for_project(pipeline):
|
|
|
32
32
|
- Fall back on the version in the deprecated '.platform-helper-version' file
|
|
33
33
|
"""
|
|
34
34
|
try:
|
|
35
|
-
|
|
35
|
+
PlatformHelperVersioning().get_required_version(pipeline)
|
|
36
36
|
except PlatformException as err:
|
|
37
37
|
ClickIOProvider().abort_with_error(str(err))
|
|
@@ -12,12 +12,16 @@ from dbt_platform_helper.platform_exception import PlatformException
|
|
|
12
12
|
from dbt_platform_helper.providers.files import FileProvider
|
|
13
13
|
from dbt_platform_helper.providers.io import ClickIOProvider
|
|
14
14
|
from dbt_platform_helper.utils.application import Application
|
|
15
|
-
from dbt_platform_helper.utils.application import
|
|
15
|
+
from dbt_platform_helper.utils.application import (
|
|
16
|
+
ApplicationEnvironmentNotFoundException,
|
|
17
|
+
)
|
|
16
18
|
from dbt_platform_helper.utils.application import load_application
|
|
17
19
|
from dbt_platform_helper.utils.aws import check_image_exists
|
|
18
20
|
from dbt_platform_helper.utils.aws import get_aws_session_or_abort
|
|
19
21
|
from dbt_platform_helper.utils.aws import get_build_url_from_arn
|
|
20
22
|
from dbt_platform_helper.utils.aws import get_build_url_from_pipeline_execution_id
|
|
23
|
+
from dbt_platform_helper.utils.aws import get_image_build_project
|
|
24
|
+
from dbt_platform_helper.utils.aws import get_manual_release_pipeline
|
|
21
25
|
from dbt_platform_helper.utils.aws import list_latest_images
|
|
22
26
|
from dbt_platform_helper.utils.aws import start_build_extraction
|
|
23
27
|
from dbt_platform_helper.utils.aws import start_pipeline_and_return_execution_id
|
|
@@ -32,6 +36,8 @@ class Codebase:
|
|
|
32
36
|
load_application: Callable[[str], Application] = load_application,
|
|
33
37
|
get_aws_session_or_abort: Callable[[str], Session] = get_aws_session_or_abort,
|
|
34
38
|
check_image_exists: Callable[[str], str] = check_image_exists,
|
|
39
|
+
get_image_build_project: Callable[[str], str] = get_image_build_project,
|
|
40
|
+
get_manual_release_pipeline: Callable[[str], str] = get_manual_release_pipeline,
|
|
35
41
|
get_build_url_from_arn: Callable[[str], str] = get_build_url_from_arn,
|
|
36
42
|
get_build_url_from_pipeline_execution_id: Callable[
|
|
37
43
|
[str], str
|
|
@@ -48,6 +54,8 @@ class Codebase:
|
|
|
48
54
|
self.load_application = load_application
|
|
49
55
|
self.get_aws_session_or_abort = get_aws_session_or_abort
|
|
50
56
|
self.check_image_exists = check_image_exists
|
|
57
|
+
self.get_image_build_project = get_image_build_project
|
|
58
|
+
self.get_manual_release_pipeline = get_manual_release_pipeline
|
|
51
59
|
self.get_build_url_from_arn = get_build_url_from_arn
|
|
52
60
|
self.get_build_url_from_pipeline_execution_id = get_build_url_from_pipeline_execution_id
|
|
53
61
|
self.list_latest_images = list_latest_images
|
|
@@ -124,12 +132,13 @@ class Codebase:
|
|
|
124
132
|
self.check_if_commit_exists(commit)
|
|
125
133
|
|
|
126
134
|
codebuild_client = session.client("codebuild")
|
|
135
|
+
project_name = self.get_image_build_project(codebuild_client, app, codebase)
|
|
127
136
|
build_url = self.__start_build_with_confirmation(
|
|
128
137
|
codebuild_client,
|
|
129
138
|
self.get_build_url_from_arn,
|
|
130
139
|
f'You are about to build "{app}" for "{codebase}" with commit "{commit}". Do you want to continue?',
|
|
131
140
|
{
|
|
132
|
-
"projectName":
|
|
141
|
+
"projectName": project_name,
|
|
133
142
|
"artifactsOverride": {"type": "NO_ARTIFACTS"},
|
|
134
143
|
"sourceVersion": commit,
|
|
135
144
|
},
|
|
@@ -148,13 +157,14 @@ class Codebase:
|
|
|
148
157
|
|
|
149
158
|
application = self.load_application(app, default_session=session)
|
|
150
159
|
if not application.environments.get(env):
|
|
151
|
-
raise ApplicationEnvironmentNotFoundException(env)
|
|
160
|
+
raise ApplicationEnvironmentNotFoundException(application.name, env)
|
|
152
161
|
|
|
153
162
|
self.check_image_exists(session, application, codebase, commit)
|
|
154
163
|
|
|
155
|
-
pipeline_name = f"{app}-{codebase}-manual-release-pipeline"
|
|
156
164
|
codepipeline_client = session.client("codepipeline")
|
|
157
165
|
|
|
166
|
+
pipeline_name = self.get_manual_release_pipeline(codepipeline_client, app, codebase)
|
|
167
|
+
|
|
158
168
|
build_url = self.__start_pipeline_execution_with_confirmation(
|
|
159
169
|
codepipeline_client,
|
|
160
170
|
self.get_build_url_from_pipeline_execution_id,
|
|
@@ -242,13 +252,6 @@ class ApplicationDeploymentNotTriggered(PlatformException):
|
|
|
242
252
|
super().__init__(f"""Your deployment for {codebase} was not triggered.""")
|
|
243
253
|
|
|
244
254
|
|
|
245
|
-
class ApplicationEnvironmentNotFoundException(ApplicationException):
|
|
246
|
-
def __init__(self, environment: str):
|
|
247
|
-
super().__init__(
|
|
248
|
-
f"""The environment "{environment}" either does not exist or has not been deployed."""
|
|
249
|
-
)
|
|
250
|
-
|
|
251
|
-
|
|
252
255
|
class NotInCodeBaseRepositoryException(PlatformException):
|
|
253
256
|
def __init__(self):
|
|
254
257
|
super().__init__(
|
|
@@ -0,0 +1,397 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
|
|
3
|
+
import copy
|
|
4
|
+
import json
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
from pathlib import PosixPath
|
|
7
|
+
|
|
8
|
+
import click
|
|
9
|
+
import yaml
|
|
10
|
+
from schema import SchemaError
|
|
11
|
+
|
|
12
|
+
from dbt_platform_helper.constants import PLATFORM_CONFIG_FILE
|
|
13
|
+
from dbt_platform_helper.domain.copilot_environment import CopilotTemplating
|
|
14
|
+
from dbt_platform_helper.providers.config import ConfigProvider
|
|
15
|
+
from dbt_platform_helper.providers.files import FileProvider
|
|
16
|
+
from dbt_platform_helper.providers.kms import KMSProvider
|
|
17
|
+
from dbt_platform_helper.utils.application import get_application_name
|
|
18
|
+
from dbt_platform_helper.utils.application import load_application
|
|
19
|
+
from dbt_platform_helper.utils.aws import get_aws_session_or_abort
|
|
20
|
+
from dbt_platform_helper.utils.files import generate_override_files
|
|
21
|
+
from dbt_platform_helper.utils.template import ADDON_TEMPLATE_MAP
|
|
22
|
+
from dbt_platform_helper.utils.template import camel_case
|
|
23
|
+
from dbt_platform_helper.utils.template import setup_templates
|
|
24
|
+
from dbt_platform_helper.utils.validation import validate_addons
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class Copilot:
|
|
28
|
+
|
|
29
|
+
PACKAGE_DIR = Path(__file__).resolve().parent.parent
|
|
30
|
+
|
|
31
|
+
# TODO Remove and test
|
|
32
|
+
WAF_ACL_ARN_KEY = "waf-acl-arn"
|
|
33
|
+
|
|
34
|
+
SERVICE_TYPES = [
|
|
35
|
+
"Load Balanced Web Service",
|
|
36
|
+
"Backend Service",
|
|
37
|
+
"Request-Driven Web Service",
|
|
38
|
+
"Static Site",
|
|
39
|
+
"Worker Service",
|
|
40
|
+
]
|
|
41
|
+
|
|
42
|
+
def __init__(
|
|
43
|
+
self,
|
|
44
|
+
config_provider: ConfigProvider,
|
|
45
|
+
file_provider: FileProvider,
|
|
46
|
+
copilot_templating: CopilotTemplating,
|
|
47
|
+
kms_provider: KMSProvider,
|
|
48
|
+
):
|
|
49
|
+
self.config_provider = config_provider
|
|
50
|
+
self.file_provider = file_provider
|
|
51
|
+
self.copilot_templating = copilot_templating
|
|
52
|
+
self.kms_provider = kms_provider
|
|
53
|
+
|
|
54
|
+
def list_copilot_local_environments(self):
|
|
55
|
+
return [
|
|
56
|
+
path.parent.parts[-1] for path in Path("./copilot/environments/").glob("*/manifest.yml")
|
|
57
|
+
]
|
|
58
|
+
|
|
59
|
+
def is_service(self, path: PosixPath) -> bool:
|
|
60
|
+
with open(path) as manifest_file:
|
|
61
|
+
data = yaml.safe_load(manifest_file)
|
|
62
|
+
if not data or not data.get("type"):
|
|
63
|
+
click.echo(
|
|
64
|
+
click.style(f"No type defined in manifest file {str(path)}; exiting", fg="red")
|
|
65
|
+
)
|
|
66
|
+
exit(1)
|
|
67
|
+
|
|
68
|
+
return data.get("type") in self.SERVICE_TYPES
|
|
69
|
+
|
|
70
|
+
def list_copilot_local_services(self):
|
|
71
|
+
return [
|
|
72
|
+
path.parent.parts[-1]
|
|
73
|
+
for path in Path("./copilot/").glob("*/manifest.yml")
|
|
74
|
+
if self.is_service(path)
|
|
75
|
+
]
|
|
76
|
+
|
|
77
|
+
def _validate_and_normalise_extensions_config(self, config_file, key_in_config_file=None):
|
|
78
|
+
"""Load a config file, validate it against the extensions schemas and
|
|
79
|
+
return the normalised config dict."""
|
|
80
|
+
|
|
81
|
+
def _lookup_plan(addon_type, env_conf):
|
|
82
|
+
plan = env_conf.pop("plan", None)
|
|
83
|
+
conf = addon_plans[addon_type][plan] if plan else {}
|
|
84
|
+
|
|
85
|
+
# Make a copy of the addon plan config so subsequent
|
|
86
|
+
# calls do not override the root object
|
|
87
|
+
conf = conf.copy()
|
|
88
|
+
|
|
89
|
+
conf.update(env_conf)
|
|
90
|
+
|
|
91
|
+
return conf
|
|
92
|
+
|
|
93
|
+
def _normalise_keys(source: dict):
|
|
94
|
+
return {k.replace("-", "_"): v for k, v in source.items()}
|
|
95
|
+
|
|
96
|
+
with open(self.PACKAGE_DIR / "addon-plans.yml", "r") as fd:
|
|
97
|
+
addon_plans = yaml.safe_load(fd)
|
|
98
|
+
|
|
99
|
+
# load and validate config
|
|
100
|
+
with open(config_file, "r") as fd:
|
|
101
|
+
config = yaml.safe_load(fd)
|
|
102
|
+
|
|
103
|
+
if config and key_in_config_file:
|
|
104
|
+
config = config[key_in_config_file]
|
|
105
|
+
|
|
106
|
+
# empty file
|
|
107
|
+
if not config:
|
|
108
|
+
return {}
|
|
109
|
+
|
|
110
|
+
errors = validate_addons(config)
|
|
111
|
+
|
|
112
|
+
if errors:
|
|
113
|
+
click.echo(click.style(f"Errors found in {config_file}:", fg="red"))
|
|
114
|
+
for addon, error in errors.items():
|
|
115
|
+
click.echo(click.style(f"Addon '{addon}': {error}", fg="red"))
|
|
116
|
+
exit(1)
|
|
117
|
+
|
|
118
|
+
env_names = self.list_copilot_local_environments()
|
|
119
|
+
svc_names = self.list_copilot_local_services()
|
|
120
|
+
|
|
121
|
+
if not env_names:
|
|
122
|
+
click.echo(
|
|
123
|
+
click.style(f"No environments found in ./copilot/environments; exiting", fg="red")
|
|
124
|
+
)
|
|
125
|
+
exit(1)
|
|
126
|
+
|
|
127
|
+
if not svc_names:
|
|
128
|
+
click.echo(click.style(f"No services found in ./copilot/; exiting", fg="red"))
|
|
129
|
+
exit(1)
|
|
130
|
+
|
|
131
|
+
normalised_config = {}
|
|
132
|
+
config_has_errors = False
|
|
133
|
+
for addon_name, addon_config in config.items():
|
|
134
|
+
addon_type = addon_config["type"]
|
|
135
|
+
normalised_config[addon_name] = copy.deepcopy(addon_config)
|
|
136
|
+
|
|
137
|
+
if "services" in normalised_config[addon_name]:
|
|
138
|
+
if normalised_config[addon_name]["services"] == "__all__":
|
|
139
|
+
normalised_config[addon_name]["services"] = svc_names
|
|
140
|
+
|
|
141
|
+
if not set(normalised_config[addon_name]["services"]).issubset(set(svc_names)):
|
|
142
|
+
click.echo(
|
|
143
|
+
click.style(
|
|
144
|
+
f"Services listed in {addon_name}.services do not exist in ./copilot/",
|
|
145
|
+
fg="red",
|
|
146
|
+
),
|
|
147
|
+
)
|
|
148
|
+
config_has_errors = True
|
|
149
|
+
|
|
150
|
+
environments = normalised_config[addon_name].pop("environments", {})
|
|
151
|
+
default = environments.pop("*", environments.pop("default", {}))
|
|
152
|
+
|
|
153
|
+
initial = _lookup_plan(addon_type, default)
|
|
154
|
+
|
|
155
|
+
missing_envs = set(environments.keys()) - set(env_names)
|
|
156
|
+
if missing_envs:
|
|
157
|
+
click.echo(
|
|
158
|
+
click.style(
|
|
159
|
+
f"Environment keys listed in {addon_name} do not match those defined in ./copilot/environments.",
|
|
160
|
+
fg="red",
|
|
161
|
+
)
|
|
162
|
+
),
|
|
163
|
+
click.echo(
|
|
164
|
+
click.style(
|
|
165
|
+
f" Missing environments: {', '.join(sorted(missing_envs))}",
|
|
166
|
+
fg="white",
|
|
167
|
+
),
|
|
168
|
+
)
|
|
169
|
+
config_has_errors = True
|
|
170
|
+
|
|
171
|
+
if config_has_errors:
|
|
172
|
+
continue
|
|
173
|
+
|
|
174
|
+
normalised_environments = {}
|
|
175
|
+
|
|
176
|
+
for env in env_names:
|
|
177
|
+
normalised_environments[env] = _normalise_keys(initial)
|
|
178
|
+
|
|
179
|
+
for env_name, env_config in environments.items():
|
|
180
|
+
if env_config is None:
|
|
181
|
+
env_config = {}
|
|
182
|
+
normalised_environments[env_name].update(
|
|
183
|
+
_lookup_plan(addon_type, _normalise_keys(env_config))
|
|
184
|
+
)
|
|
185
|
+
|
|
186
|
+
normalised_config[addon_name]["environments"] = normalised_environments
|
|
187
|
+
|
|
188
|
+
if config_has_errors:
|
|
189
|
+
exit(1)
|
|
190
|
+
|
|
191
|
+
return normalised_config
|
|
192
|
+
|
|
193
|
+
def get_log_destination_arn(self):
|
|
194
|
+
"""Get destination arns stored in param store in projects aws
|
|
195
|
+
account."""
|
|
196
|
+
session = get_aws_session_or_abort()
|
|
197
|
+
client = session.client("ssm", region_name="eu-west-2")
|
|
198
|
+
response = client.get_parameters(Names=["/copilot/tools/central_log_groups"])
|
|
199
|
+
|
|
200
|
+
if not response["Parameters"]:
|
|
201
|
+
click.echo(
|
|
202
|
+
click.style(
|
|
203
|
+
"No aws central log group defined in Parameter Store at location /copilot/tools/central_log_groups; exiting",
|
|
204
|
+
fg="red",
|
|
205
|
+
)
|
|
206
|
+
)
|
|
207
|
+
exit(1)
|
|
208
|
+
|
|
209
|
+
destination_arns = json.loads(response["Parameters"][0]["Value"])
|
|
210
|
+
return destination_arns
|
|
211
|
+
|
|
212
|
+
def _generate_svc_overrides(self, base_path, templates, name):
|
|
213
|
+
click.echo(f"\n>>> Generating service overrides for {name}\n")
|
|
214
|
+
overrides_path = base_path.joinpath(f"copilot/{name}/overrides")
|
|
215
|
+
overrides_path.mkdir(parents=True, exist_ok=True)
|
|
216
|
+
overrides_file = overrides_path.joinpath("cfn.patches.yml")
|
|
217
|
+
overrides_file.write_text(templates.get_template("svc/overrides/cfn.patches.yml").render())
|
|
218
|
+
|
|
219
|
+
def _get_s3_kms_alias_arns(self, session, application_name, config):
|
|
220
|
+
application = load_application(application_name, session)
|
|
221
|
+
arns = {}
|
|
222
|
+
|
|
223
|
+
for environment_name in application.environments:
|
|
224
|
+
if environment_name not in config:
|
|
225
|
+
continue
|
|
226
|
+
|
|
227
|
+
if "bucket_name" not in config[environment_name]:
|
|
228
|
+
continue
|
|
229
|
+
|
|
230
|
+
bucket_name = config[environment_name]["bucket_name"]
|
|
231
|
+
kms_client = application.environments[environment_name].session.client("kms")
|
|
232
|
+
alias_name = f"alias/{application_name}-{environment_name}-{bucket_name}-key"
|
|
233
|
+
|
|
234
|
+
try:
|
|
235
|
+
response = kms_client.describe_key(KeyId=alias_name)
|
|
236
|
+
except kms_client.exceptions.NotFoundException:
|
|
237
|
+
pass
|
|
238
|
+
else:
|
|
239
|
+
arns[environment_name] = response["KeyMetadata"]["Arn"]
|
|
240
|
+
|
|
241
|
+
return arns
|
|
242
|
+
|
|
243
|
+
def make_addons(self):
|
|
244
|
+
self.config_provider.config_file_check()
|
|
245
|
+
try:
|
|
246
|
+
config = self.config_provider.load_and_validate_platform_config()
|
|
247
|
+
except SchemaError as ex:
|
|
248
|
+
click.secho(f"Invalid `{PLATFORM_CONFIG_FILE}` file: {str(ex)}", fg="red")
|
|
249
|
+
raise click.Abort
|
|
250
|
+
|
|
251
|
+
templates = setup_templates()
|
|
252
|
+
extensions = self._get_extensions()
|
|
253
|
+
session = get_aws_session_or_abort()
|
|
254
|
+
|
|
255
|
+
application_name = get_application_name()
|
|
256
|
+
|
|
257
|
+
click.echo("\n>>> Generating Terraform compatible addons CloudFormation\n")
|
|
258
|
+
|
|
259
|
+
output_dir = Path(".").absolute()
|
|
260
|
+
env_path = Path(f"copilot/environments/")
|
|
261
|
+
env_addons_path = env_path / "addons"
|
|
262
|
+
env_overrides_path = env_path / "overrides"
|
|
263
|
+
|
|
264
|
+
self._cleanup_old_files(extensions, output_dir, env_addons_path, env_overrides_path)
|
|
265
|
+
self._generate_env_overrides(output_dir)
|
|
266
|
+
|
|
267
|
+
svc_names = self.list_copilot_local_services()
|
|
268
|
+
base_path = Path(".")
|
|
269
|
+
for svc_name in svc_names:
|
|
270
|
+
self._generate_svc_overrides(base_path, templates, svc_name)
|
|
271
|
+
|
|
272
|
+
services = []
|
|
273
|
+
for ext_name, ext_data in extensions.items():
|
|
274
|
+
extension = {**ext_data}
|
|
275
|
+
addon_type = extension.pop("type")
|
|
276
|
+
environments = extension.pop("environments")
|
|
277
|
+
environment_addon_config = {
|
|
278
|
+
"addon_type": addon_type,
|
|
279
|
+
"environments": environments,
|
|
280
|
+
"name": extension.get("name", None) or ext_name,
|
|
281
|
+
"prefix": camel_case(ext_name),
|
|
282
|
+
"secret_name": ext_name.upper().replace("-", "_"),
|
|
283
|
+
**extension,
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
services.append(environment_addon_config)
|
|
287
|
+
|
|
288
|
+
service_addon_config = {
|
|
289
|
+
"application_name": application_name,
|
|
290
|
+
"name": extension.get("name", None) or ext_name,
|
|
291
|
+
"prefix": camel_case(ext_name),
|
|
292
|
+
"environments": environments,
|
|
293
|
+
**extension,
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
log_destination_arns = self.get_log_destination_arn()
|
|
297
|
+
|
|
298
|
+
if addon_type in ["s3", "s3-policy"]:
|
|
299
|
+
if extensions[ext_name].get("serve_static_content"):
|
|
300
|
+
continue
|
|
301
|
+
|
|
302
|
+
s3_kms_arns = self._get_s3_kms_alias_arns(session, application_name, environments)
|
|
303
|
+
for environment_name in environments:
|
|
304
|
+
environments[environment_name]["kms_key_arn"] = s3_kms_arns.get(
|
|
305
|
+
environment_name, "kms-key-not-found"
|
|
306
|
+
)
|
|
307
|
+
|
|
308
|
+
self._generate_service_addons(
|
|
309
|
+
extension,
|
|
310
|
+
ext_name,
|
|
311
|
+
addon_type,
|
|
312
|
+
output_dir,
|
|
313
|
+
service_addon_config,
|
|
314
|
+
templates,
|
|
315
|
+
log_destination_arns,
|
|
316
|
+
)
|
|
317
|
+
|
|
318
|
+
environments = self.config_provider.apply_environment_defaults(config)["environments"]
|
|
319
|
+
|
|
320
|
+
self.copilot_templating.generate_cross_account_s3_policies(environments, extensions)
|
|
321
|
+
|
|
322
|
+
click.echo(templates.get_template("addon-instructions.txt").render(services=services))
|
|
323
|
+
|
|
324
|
+
def _get_extensions(self):
|
|
325
|
+
config = self._validate_and_normalise_extensions_config(
|
|
326
|
+
self.PACKAGE_DIR / "default-extensions.yml"
|
|
327
|
+
)
|
|
328
|
+
project_config = self._validate_and_normalise_extensions_config(
|
|
329
|
+
PLATFORM_CONFIG_FILE, "extensions"
|
|
330
|
+
)
|
|
331
|
+
config.update(project_config)
|
|
332
|
+
return config
|
|
333
|
+
|
|
334
|
+
def _generate_env_overrides(self, output_dir):
|
|
335
|
+
path = "templates/env/terraform-overrides"
|
|
336
|
+
click.echo("\n>>> Generating Environment overrides\n")
|
|
337
|
+
overrides_path = output_dir.joinpath(f"copilot/environments/overrides")
|
|
338
|
+
overrides_path.mkdir(parents=True, exist_ok=True)
|
|
339
|
+
template_overrides_path = Path(__file__).parent.parent.joinpath(path)
|
|
340
|
+
generate_override_files(Path("."), template_overrides_path, overrides_path)
|
|
341
|
+
|
|
342
|
+
def _generate_service_addons(
|
|
343
|
+
self,
|
|
344
|
+
addon_config,
|
|
345
|
+
addon_name,
|
|
346
|
+
addon_type,
|
|
347
|
+
output_dir,
|
|
348
|
+
service_addon_config,
|
|
349
|
+
templates,
|
|
350
|
+
log_destination_arns,
|
|
351
|
+
):
|
|
352
|
+
# generate svc addons
|
|
353
|
+
for addon_template in ADDON_TEMPLATE_MAP.get(addon_type, []):
|
|
354
|
+
template = templates.get_template(addon_template)
|
|
355
|
+
|
|
356
|
+
for svc in addon_config.get("services", []):
|
|
357
|
+
service_path = Path(f"copilot/{svc}/addons/")
|
|
358
|
+
|
|
359
|
+
contents = template.render(
|
|
360
|
+
{
|
|
361
|
+
"addon_config": service_addon_config,
|
|
362
|
+
"log_destination": log_destination_arns,
|
|
363
|
+
}
|
|
364
|
+
)
|
|
365
|
+
|
|
366
|
+
(output_dir / service_path).mkdir(parents=True, exist_ok=True)
|
|
367
|
+
click.echo(
|
|
368
|
+
self.file_provider.mkfile(
|
|
369
|
+
output_dir, service_path / f"{addon_name}.yml", contents, overwrite=True
|
|
370
|
+
)
|
|
371
|
+
)
|
|
372
|
+
|
|
373
|
+
def _cleanup_old_files(self, config, output_dir, env_addons_path, env_overrides_path):
|
|
374
|
+
def _rmdir(path):
|
|
375
|
+
if not path.exists():
|
|
376
|
+
return
|
|
377
|
+
for f in path.iterdir():
|
|
378
|
+
if f.is_file():
|
|
379
|
+
f.unlink()
|
|
380
|
+
if f.is_dir():
|
|
381
|
+
_rmdir(f)
|
|
382
|
+
f.rmdir()
|
|
383
|
+
|
|
384
|
+
_rmdir(output_dir / env_addons_path)
|
|
385
|
+
_rmdir(output_dir / env_overrides_path)
|
|
386
|
+
|
|
387
|
+
all_services = set()
|
|
388
|
+
for services in [v["services"] for v in config.values() if "services" in v]:
|
|
389
|
+
all_services.update(services)
|
|
390
|
+
|
|
391
|
+
for service in all_services:
|
|
392
|
+
svc_addons_dir = Path(output_dir, "copilot", service, "addons")
|
|
393
|
+
if not svc_addons_dir.exists():
|
|
394
|
+
continue
|
|
395
|
+
for f in svc_addons_dir.iterdir():
|
|
396
|
+
if f.is_file():
|
|
397
|
+
f.unlink()
|
|
@@ -10,9 +10,7 @@ from dbt_platform_helper.providers.cloudformation import CloudFormation
|
|
|
10
10
|
from dbt_platform_helper.providers.config import ConfigProvider
|
|
11
11
|
from dbt_platform_helper.providers.files import FileProvider
|
|
12
12
|
from dbt_platform_helper.providers.io import ClickIOProvider
|
|
13
|
-
from dbt_platform_helper.providers.load_balancers import
|
|
14
|
-
get_https_certificate_for_application,
|
|
15
|
-
)
|
|
13
|
+
from dbt_platform_helper.providers.load_balancers import LoadBalancerProvider
|
|
16
14
|
from dbt_platform_helper.providers.vpc import Vpc
|
|
17
15
|
from dbt_platform_helper.providers.vpc import VpcNotFoundForNameException
|
|
18
16
|
from dbt_platform_helper.providers.vpc import VpcProvider
|
|
@@ -27,9 +25,10 @@ class CopilotEnvironment:
|
|
|
27
25
|
config_provider: ConfigProvider,
|
|
28
26
|
vpc_provider: VpcProvider = None,
|
|
29
27
|
cloudformation_provider: CloudFormation = None,
|
|
30
|
-
session: Session = None, # TODO - this is a temporary fix, will fall away once
|
|
28
|
+
session: Session = None, # TODO - this is a temporary fix, will fall away once _get_environment_vpc is updated.
|
|
31
29
|
copilot_templating=None,
|
|
32
30
|
io: ClickIOProvider = ClickIOProvider(),
|
|
31
|
+
load_balancer_provider: LoadBalancerProvider = LoadBalancerProvider,
|
|
33
32
|
):
|
|
34
33
|
self.config_provider = config_provider
|
|
35
34
|
self.vpc_provider = vpc_provider
|
|
@@ -38,6 +37,7 @@ class CopilotEnvironment:
|
|
|
38
37
|
)
|
|
39
38
|
self.io = io
|
|
40
39
|
self.session = session
|
|
40
|
+
self.load_balancer = load_balancer_provider(session)
|
|
41
41
|
self.cloudformation_provider = cloudformation_provider
|
|
42
42
|
|
|
43
43
|
def generate(self, environment_name: str) -> None:
|
|
@@ -56,8 +56,8 @@ class CopilotEnvironment:
|
|
|
56
56
|
|
|
57
57
|
app_name = platform_config["application"]
|
|
58
58
|
|
|
59
|
-
certificate_arn = get_https_certificate_for_application(
|
|
60
|
-
|
|
59
|
+
certificate_arn = self.load_balancer.get_https_certificate_for_application(
|
|
60
|
+
app_name, environment_name
|
|
61
61
|
)
|
|
62
62
|
|
|
63
63
|
vpc = self._get_environment_vpc(
|