dbt-platform-helper 15.6.0__py3-none-any.whl → 15.8.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.

@@ -27,6 +27,8 @@
27
27
  - [platform-helper secrets](#platform-helper-secrets)
28
28
  - [platform-helper secrets copy](#platform-helper-secrets-copy)
29
29
  - [platform-helper secrets list](#platform-helper-secrets-list)
30
+ - [platform-helper service](#platform-helper-service)
31
+ - [platform-helper service generate](#platform-helper-service-generate)
30
32
  - [platform-helper notify](#platform-helper-notify)
31
33
  - [platform-helper notify environment-progress](#platform-helper-notify-environment-progress)
32
34
  - [platform-helper notify post-message](#platform-helper-notify-post-message)
@@ -66,6 +68,7 @@ platform-helper <command> [--version]
66
68
  - [`notify` ↪](#platform-helper-notify)
67
69
  - [`pipeline` ↪](#platform-helper-pipeline)
68
70
  - [`secrets` ↪](#platform-helper-secrets)
71
+ - [`service` ↪](#platform-helper-service)
69
72
  - [`version` ↪](#platform-helper-version)
70
73
 
71
74
  # platform-helper application
@@ -660,6 +663,54 @@ platform-helper secrets list <application> <environment>
660
663
  - `--help <boolean>` _Defaults to False._
661
664
  - Show this message and exit.
662
665
 
666
+ # platform-helper service
667
+
668
+ [↩ Parent](#platform-helper)
669
+
670
+ Commands affecting services.
671
+
672
+ ## Usage
673
+
674
+ ```
675
+ platform-helper service generate
676
+ ```
677
+
678
+ ## Options
679
+
680
+ - `--help <boolean>` _Defaults to False._
681
+ - Show this message and exit.
682
+
683
+ ## Commands
684
+
685
+ - [`generate` ↪](#platform-helper-service-generate)
686
+
687
+ # platform-helper service generate
688
+
689
+ [↩ Parent](#platform-helper-service)
690
+
691
+ Generate terraform manifest for the specified service(s).
692
+
693
+ ## Usage
694
+
695
+ ```
696
+ platform-helper service generate [--name <name>] [--environment <environment>]
697
+ [--image-tag <image_tag>]
698
+ ```
699
+
700
+ ## Options
701
+
702
+ - `--name
703
+ -n <text>`
704
+ - The name of the service to generate a manifest for. Multiple values accepted.
705
+ - `--environment
706
+ -e <text>`
707
+ - The name of the environment to generate service manifests for. Multiple values accepted.
708
+ - `--image-tag
709
+ -i <text>`
710
+ - Docker image tag to deploy for the service. Overrides the $IMAGE_TAG environment variable.
711
+ - `--help <boolean>` _Defaults to False._
712
+ - Show this message and exit.
713
+
663
714
  # platform-helper notify
664
715
 
665
716
  [↩ Parent](#platform-helper)
@@ -281,12 +281,13 @@ def task_stats(env, app, disk, storage, network):
281
281
  memory,
282
282
  dsk,
283
283
  ) in cpu_response["results"]:
284
+ cpu_value = f"{float(cpu['value']):.1f}" + "%"
284
285
  values = [
285
286
  f"{YELLOW}{taskdef['value'].split('-')[-1]}",
286
287
  f"{YELLOW}{task['value']}",
287
288
  f"{YELLOW}{task_def_revision['value']}",
288
289
  f"{YELLOW}{status['value']}",
289
- f"{YELLOW}{'%.1f' % float(cpu['value']) + '%'}",
290
+ f"{YELLOW}{cpu_value}",
290
291
  f"{YELLOW}{memory['value'] + 'M'}",
291
292
  ]
292
293
 
@@ -4,7 +4,6 @@ from pathlib import Path
4
4
 
5
5
  import click
6
6
  from botocore.exceptions import ClientError
7
- from cloudfoundry_client.client import CloudFoundryClient
8
7
 
9
8
  from dbt_platform_helper.domain.versioning import PlatformHelperVersioning
10
9
  from dbt_platform_helper.utils.application import get_application_name
@@ -19,25 +18,6 @@ def secret_should_be_skipped(secret_name):
19
18
  return "AWS_" in secret_name
20
19
 
21
20
 
22
- def get_paas_env_vars(client: CloudFoundryClient, paas: str) -> dict:
23
- org, space, app = paas.split("/")
24
-
25
- env_vars = None
26
-
27
- for paas_org in client.v2.organizations:
28
- if paas_org["entity"]["name"] == org:
29
- for paas_space in paas_org.spaces():
30
- if paas_space["entity"]["name"] == space:
31
- for paas_app in paas_space.apps():
32
- if paas_app["entity"]["name"] == app:
33
- env_vars = paas_app["entity"]["environment_json"]
34
-
35
- if not env_vars:
36
- raise Exception(f"Application {paas} not found")
37
-
38
- return dict(env_vars)
39
-
40
-
41
21
  @click.group(chain=True, cls=ClickDocOptGroup)
42
22
  def secrets():
43
23
  PlatformHelperVersioning().check_if_needs_update()
@@ -0,0 +1,53 @@
1
+ import click
2
+
3
+ from dbt_platform_helper.domain.service import ServiceManager
4
+ from dbt_platform_helper.domain.versioning import PlatformHelperVersioning
5
+ from dbt_platform_helper.platform_exception import PlatformException
6
+ from dbt_platform_helper.providers.io import ClickIOProvider
7
+ from dbt_platform_helper.utils.click import ClickDocOptGroup
8
+
9
+
10
+ @click.group(cls=ClickDocOptGroup)
11
+ def service():
12
+ """Commands affecting services."""
13
+ PlatformHelperVersioning().check_if_needs_update()
14
+
15
+
16
+ @service.command(help="Generate terraform manifest for the specified service(s).")
17
+ @click.option(
18
+ "--name",
19
+ "-n",
20
+ required=False,
21
+ help="The name of the service to generate a manifest for. Multiple values accepted.",
22
+ multiple=True,
23
+ )
24
+ @click.option(
25
+ "--environment",
26
+ "-e",
27
+ required=False,
28
+ multiple=True,
29
+ help="The name of the environment to generate service manifests for. Multiple values accepted.",
30
+ )
31
+ @click.option(
32
+ "--image-tag",
33
+ "-i",
34
+ required=False,
35
+ help="Docker image tag to deploy for the service. Overrides the $IMAGE_TAG environment variable.",
36
+ )
37
+ def generate(name, environment, image_tag):
38
+ """Validates the service-config.yml format, applies the environment-specific
39
+ overrides, and generates a Terraform manifest at
40
+ /terraform/services/<environment>/<service>/main.tf.json."""
41
+
42
+ services = list(name)
43
+ environments = list(environment)
44
+ click_io = ClickIOProvider()
45
+
46
+ try:
47
+ service_manager = ServiceManager()
48
+ service_manager.generate(
49
+ environments=environments, services=services, image_tag_flag=image_tag
50
+ )
51
+
52
+ except PlatformException as err:
53
+ click_io.abort_with_error(str(err))
@@ -1,5 +1,7 @@
1
1
  # TODO: DBTP-1888: Move to Config provider
2
2
  PLATFORM_CONFIG_FILE = "platform-config.yml"
3
+ SERVICE_DIRECTORY = "services"
4
+ SERVICE_CONFIG_FILE = "service-config.yml"
3
5
  PLATFORM_HELPER_VERSION_OVERRIDE_KEY = "PLATFORM_HELPER_VERSION_OVERRIDE"
4
6
  TERRAFORM_EXTENSIONS_MODULE_SOURCE_OVERRIDE_ENV_VAR = "TERRAFORM_EXTENSIONS_MODULE_SOURCE_OVERRIDE"
5
7
  TERRAFORM_ENVIRONMENT_PIPELINES_MODULE_SOURCE_OVERRIDE_ENV_VAR = (
@@ -8,6 +10,11 @@ TERRAFORM_ENVIRONMENT_PIPELINES_MODULE_SOURCE_OVERRIDE_ENV_VAR = (
8
10
  TERRAFORM_CODEBASE_PIPELINES_MODULE_SOURCE_OVERRIDE_ENV_VAR = (
9
11
  "TERRAFORM_CODEBASE_PIPELINES_MODULE_SOURCE_OVERRIDE"
10
12
  )
13
+ TERRAFORM_ECS_SERVICE_MODULE_SOURCE_OVERRIDE_ENV_VAR = (
14
+ "TERRAFORM_ECS_SERVICE_MODULE_SOURCE_OVERRIDE"
15
+ )
16
+ TERRAFORM_MODULE_SOURCE_TYPE_ENV_VAR = "TERRAFORM_MODULE_SOURCE_TYPE" # "LOCAL", "SSH", "OVERRIDE"
17
+ IMAGE_TAG_ENV_VAR = "IMAGE_TAG"
11
18
  PLATFORM_HELPER_PACKAGE_NAME = "dbt-platform-helper"
12
19
  SUPPORTED_TERRAFORM_VERSION = "~> 1.8"
13
20
  SUPPORTED_AWS_PROVIDER_VERSION = "~> 5"
@@ -15,6 +22,7 @@ FIRST_UPGRADABLE_PLATFORM_HELPER_MAJOR_VERSION = 13
15
22
 
16
23
  MERGED_TPM_PLATFORM_HELPER_VERSION = 14
17
24
  PLATFORM_CONFIG_SCHEMA_VERSION = 1
25
+ SERVICE_CONFIG_SCHEMA_VERSION = 1
18
26
 
19
27
  # Keys
20
28
  CODEBASE_PIPELINES_KEY = "codebase_pipelines"
@@ -0,0 +1,153 @@
1
+ from datetime import datetime
2
+ from importlib.metadata import version
3
+ from pathlib import Path
4
+
5
+ from dbt_platform_helper.constants import IMAGE_TAG_ENV_VAR
6
+ from dbt_platform_helper.constants import PLATFORM_HELPER_VERSION_OVERRIDE_KEY
7
+ from dbt_platform_helper.constants import SERVICE_CONFIG_FILE
8
+ from dbt_platform_helper.constants import SERVICE_DIRECTORY
9
+ from dbt_platform_helper.constants import (
10
+ TERRAFORM_ECS_SERVICE_MODULE_SOURCE_OVERRIDE_ENV_VAR,
11
+ )
12
+ from dbt_platform_helper.constants import TERRAFORM_MODULE_SOURCE_TYPE_ENV_VAR
13
+ from dbt_platform_helper.domain.terraform_environment import (
14
+ EnvironmentNotFoundException,
15
+ )
16
+ from dbt_platform_helper.entities.service import ServiceConfig
17
+ from dbt_platform_helper.platform_exception import PlatformException
18
+ from dbt_platform_helper.providers.config import ConfigLoader
19
+ from dbt_platform_helper.providers.config import ConfigProvider
20
+ from dbt_platform_helper.providers.config_validator import ConfigValidator
21
+ from dbt_platform_helper.providers.environment_variable import (
22
+ EnvironmentVariableProvider,
23
+ )
24
+ from dbt_platform_helper.providers.io import ClickIOProvider
25
+ from dbt_platform_helper.providers.terraform_manifest import TerraformManifestProvider
26
+ from dbt_platform_helper.providers.yaml_file import YamlFileProvider
27
+ from dbt_platform_helper.utils.application import load_application
28
+ from dbt_platform_helper.utils.deep_merge import deep_merge
29
+
30
+ # TODO add schema version too service config
31
+
32
+
33
+ class ServiceManager:
34
+ def __init__(
35
+ self,
36
+ config_provider=ConfigProvider(ConfigValidator()),
37
+ loader: ConfigLoader = ConfigLoader(),
38
+ io: ClickIOProvider = ClickIOProvider(),
39
+ file_provider=YamlFileProvider,
40
+ environment_variable_provider: EnvironmentVariableProvider = None,
41
+ manifest_provider: TerraformManifestProvider = None,
42
+ platform_helper_version_override: str = None,
43
+ load_application=load_application,
44
+ ):
45
+
46
+ self.file_provider = file_provider
47
+ self.config_provider = config_provider
48
+ self.loader = loader
49
+ self.io = io
50
+ self.environment_variable_provider = (
51
+ environment_variable_provider or EnvironmentVariableProvider()
52
+ )
53
+ self.manifest_provider = manifest_provider or TerraformManifestProvider()
54
+ self.platform_helper_version_override = (
55
+ platform_helper_version_override
56
+ or self.environment_variable_provider.get(PLATFORM_HELPER_VERSION_OVERRIDE_KEY)
57
+ )
58
+ self.load_application = load_application
59
+
60
+ def generate(self, environments: list[str], services: list[str], image_tag_flag: str = None):
61
+
62
+ config = self.config_provider.get_enriched_config()
63
+ application_name = config.get("application", "")
64
+ application = self.load_application(app=application_name)
65
+
66
+ if not environments:
67
+ for environment in application.environments:
68
+ environments.append(environment)
69
+ else:
70
+ for environment in environments:
71
+ if environment not in application.environments:
72
+ raise EnvironmentNotFoundException(
73
+ f"cannot generate terraform for environment {environment}. It does not exist in your configuration"
74
+ )
75
+
76
+ if not services:
77
+ try:
78
+ for dir in Path("services").iterdir():
79
+ if dir.is_dir():
80
+ config_path = dir / SERVICE_CONFIG_FILE
81
+ if config_path.exists():
82
+ services.append(dir.name)
83
+ else:
84
+ self.io.warn(
85
+ f"Failed loading service name from {dir.name}.\n"
86
+ "Please ensure that your '/services' directory follows the correct structure (i.e. /services/<service_name>/service-config.yml) and the 'service-config.yml' contents are correct."
87
+ )
88
+ except Exception as e:
89
+ self.io.abort_with_error(f"Failed extracting services with exception, {e}")
90
+ service_models = []
91
+ for service in services:
92
+ service_models.append(
93
+ self.loader.load_into_model(
94
+ f"{SERVICE_DIRECTORY}/{service}/{SERVICE_CONFIG_FILE}",
95
+ ServiceConfig,
96
+ )
97
+ )
98
+
99
+ platform_helper_version_for_template: str = (
100
+ self.platform_helper_version_override
101
+ or config.get("default_versions", {}).get("platform-helper")
102
+ )
103
+
104
+ source_type = self.environment_variable_provider.get(TERRAFORM_MODULE_SOURCE_TYPE_ENV_VAR)
105
+
106
+ if source_type == "LOCAL":
107
+ module_source_override = ServiceConfig.local_terraform_source
108
+ elif source_type == "OVERRIDE":
109
+ module_source_override = self.environment_variable_provider.get(
110
+ TERRAFORM_ECS_SERVICE_MODULE_SOURCE_OVERRIDE_ENV_VAR
111
+ )
112
+ else:
113
+ module_source_override = None
114
+
115
+ image_tag = image_tag_flag or self.environment_variable_provider.get(IMAGE_TAG_ENV_VAR)
116
+ if not image_tag:
117
+ raise PlatformException(
118
+ f"An image tag must be provided to deploy a service. This can be set by the $IMAGE_TAG environment variable, or the --image-tag flag."
119
+ )
120
+
121
+ timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
122
+
123
+ for service in service_models:
124
+
125
+ for environment in environments:
126
+
127
+ model_dump = service.model_dump(exclude_none=True)
128
+ env_overrides = model_dump.get("environments", {}).get(environment)
129
+ if env_overrides:
130
+ merged_config = deep_merge(model_dump, env_overrides)
131
+ else:
132
+ merged_config = model_dump.copy()
133
+ merged_config.pop("environments", None)
134
+
135
+ output_path = Path(
136
+ f"terraform/{SERVICE_DIRECTORY}/{environment}/{service.name}/{SERVICE_CONFIG_FILE}"
137
+ )
138
+ output_path.parent.mkdir(parents=True, exist_ok=True)
139
+
140
+ self.file_provider.write(
141
+ str(output_path),
142
+ merged_config,
143
+ f"# WARNING: This is an autogenerated file, not for manual editing.\n# Generated by platform-helper {version('dbt-platform-helper')} / {timestamp}.\n",
144
+ )
145
+
146
+ self.manifest_provider.generate_service_config(
147
+ service,
148
+ environment,
149
+ image_tag,
150
+ platform_helper_version_for_template,
151
+ config,
152
+ module_source_override,
153
+ )
@@ -152,7 +152,7 @@ class PlatformConfigSchema:
152
152
  {
153
153
  "name": str,
154
154
  Optional("requires_approval"): bool,
155
- }
155
+ },
156
156
  ],
157
157
  },
158
158
  {
@@ -167,9 +167,16 @@ class PlatformConfigSchema:
167
167
  },
168
168
  ),
169
169
  ],
170
+ Optional("cache_invalidation"): {
171
+ "domains": PlatformConfigSchema.__cache_invalidation_domains_schema(),
172
+ },
170
173
  },
171
174
  }
172
175
 
176
+ @staticmethod
177
+ def __cache_invalidation_domains_schema() -> dict:
178
+ return {str: {"paths": [str], "environment": str}}
179
+
173
180
  @staticmethod
174
181
  def __default_versions_schema() -> dict:
175
182
  return {
@@ -403,11 +410,19 @@ class PlatformConfigSchema:
403
410
  Optional("environments"): {
404
411
  Optional(PlatformConfigSchema.__valid_environment_name()): {
405
412
  "team_name": str,
406
- "contact_name": str,
407
- "contact_email": str,
408
- "documentation_url": str,
413
+ Optional("contact_name"): str,
414
+ Optional("contact_email"): str,
415
+ Optional("contacts"): [
416
+ {
417
+ "name": str,
418
+ "type": str,
419
+ "contact": str,
420
+ }
421
+ ],
422
+ Optional("documentation_url"): str,
409
423
  "services_to_monitor": dict,
410
- }
424
+ Optional("description"): str,
425
+ },
411
426
  },
412
427
  }
413
428
 
@@ -0,0 +1,114 @@
1
+ from typing import ClassVar
2
+ from typing import Dict
3
+ from typing import Optional
4
+ from typing import Union
5
+
6
+ from pydantic import BaseModel
7
+ from pydantic import Field
8
+
9
+
10
+ class HealthCheck(BaseModel):
11
+ path: Optional[str] = Field(description="""Path the healthcheck calls""", default=None)
12
+ port: Optional[int] = Field(description="""Port the healthcheck calls""", default=None)
13
+ success_codes: Optional[str] = Field(
14
+ description="""The success codes the healthcheck looks for""", default=None
15
+ )
16
+ healthy_threshold: Optional[int] = Field(description="""The number of """, default=None)
17
+ unhealthy_threshold: Optional[int] = Field(description="""The number of """, default=None)
18
+ interval: Optional[str] = Field(
19
+ description="""The interval inbetween health check calls""", default=None
20
+ )
21
+ timeout: Optional[str] = Field(
22
+ description="""The timeout for a healthcheck call""", default=None
23
+ )
24
+ grace_period: Optional[str] = Field(description="""The time""", default=None)
25
+
26
+
27
+ class Http(BaseModel):
28
+ path: Optional[str] = Field(
29
+ description="""Requests to this path will be forwarded to your service.""", default=None
30
+ )
31
+ alb: Optional[str] = Field(default=None)
32
+ target_container: Optional[str] = Field(
33
+ description="""Target container for the requests""", default=None
34
+ )
35
+ healthcheck: Optional[HealthCheck] = Field(default=None)
36
+
37
+
38
+ class Sidecar(BaseModel):
39
+ port: int = Field()
40
+ image: str = Field()
41
+ essential: Optional[bool] = Field(default=None)
42
+ variables: Optional[Dict[str, Union[str, int, bool]]] = Field(default=None)
43
+ secrets: Optional[Dict[str, str]] = Field(default=None)
44
+
45
+
46
+ class SidecarOverride(BaseModel):
47
+ port: Optional[int] = Field(default=None)
48
+ image: Optional[str] = Field(default=None)
49
+ essential: Optional[bool] = Field(default=None)
50
+ variables: Optional[Dict[str, Union[str, int, bool]]] = Field(default=None)
51
+ secrets: Optional[Dict[str, str]] = Field(default=None)
52
+
53
+
54
+ class Image(BaseModel):
55
+ build: Optional[str] = Field(default=None)
56
+ location: Optional[str] = Field(default=None)
57
+ port: int = Field()
58
+
59
+
60
+ class VPC(BaseModel):
61
+ placement: str = Field()
62
+
63
+
64
+ class Network(BaseModel):
65
+ connect: bool = Field(
66
+ description="Enable Service Connect for intra-environment traffic between services."
67
+ )
68
+ vpc: Optional[VPC] = Field(default=None)
69
+
70
+
71
+ class Storage(BaseModel):
72
+ readonly_fs: bool
73
+
74
+
75
+ class ServiceConfigEnvironmentOverride(BaseModel):
76
+ http: Optional[Http] = Field(default=None)
77
+ sidecars: Optional[Dict[str, SidecarOverride]] = Field(default=None)
78
+ image: Optional[Image] = Field(default=None)
79
+
80
+ cpu: Optional[int] = Field(default=None)
81
+ memory: Optional[int] = Field(default=None)
82
+ count: Optional[int] = Field(default=None)
83
+ exec: Optional[bool] = Field(default=None)
84
+ network: Optional[Network] = Field(default=None)
85
+
86
+ storage: Optional[Storage] = Field(default=None)
87
+
88
+ variables: Optional[Dict[str, Union[str, int, bool]]] = Field(default=None)
89
+ secrets: Optional[Dict[str, str]] = Field(default=None)
90
+
91
+
92
+ class ServiceConfig(BaseModel):
93
+ name: str = Field(description="""Name of the Service.""")
94
+ type: str = Field(description="""The type of service""")
95
+
96
+ http: Optional[Http] = Field(default=None) # TODO http required if service type load balancer
97
+ sidecars: Optional[Dict[str, Sidecar]] = Field(default=None)
98
+ image: Image = Field()
99
+
100
+ cpu: int = Field()
101
+ memory: int = Field()
102
+ count: int = Field()
103
+ exec: Optional[bool] = Field(default=None)
104
+ network: Optional[Network] = Field(default=None)
105
+
106
+ storage: Optional[Storage] = Field(default=None)
107
+
108
+ variables: Optional[Dict[str, Union[str, int, bool]]] = Field(default=None)
109
+ secrets: Optional[Dict[str, str]] = Field(default=None)
110
+ # Environment overrides can override almost the full config
111
+ environments: Optional[Dict[str, ServiceConfigEnvironmentOverride]] = Field(default=None)
112
+
113
+ # Class based variable used when handling the obejct
114
+ local_terraform_source: ClassVar[str] = "../../../../../platform-tools/terraform/ecs-service"
@@ -25,6 +25,34 @@ PLEASE_UPGRADE_TO_V13_MESSAGE = """Please ensure that you have already upgraded
25
25
  Then upgrade platform-helper to version {installed_platform_helper_version} and run 'platform-helper config migrate' to upgrade the configuration to the current schema version."""
26
26
 
27
27
 
28
+ class ConfigLoader:
29
+ def __init__(self, file_provider=YamlFileProvider, io: ClickIOProvider = ClickIOProvider()):
30
+ self.io = io
31
+ self.file_provider = file_provider
32
+
33
+ def load_into_model(self, path, model):
34
+ try:
35
+ file_content = self.file_provider.load(path)
36
+ return model(**file_content)
37
+ except FileNotFoundException as e:
38
+ self.io.abort_with_error(
39
+ f"{e} Please check it exists and you are in the root directory of your -deploy repository."
40
+ )
41
+ except FileProviderException as e:
42
+ self.io.abort_with_error(f"Error loading configuration from {path}: {e}")
43
+
44
+ def load(self, path):
45
+ try:
46
+ file_content = self.file_provider.load(path)
47
+ return file_content
48
+ except FileNotFoundException as e:
49
+ self.io.abort_with_error(
50
+ f"{e} Please check it exists and you are in the root directory of your -deploy repository."
51
+ )
52
+ except FileProviderException as e:
53
+ self.io.abort_with_error(f"Error loading configuration from {path}: {e}")
54
+
55
+
28
56
  class ConfigProvider:
29
57
  def __init__(
30
58
  self,
@@ -29,6 +29,7 @@ class ConfigValidator:
29
29
  self.validate_environment_pipelines_triggers,
30
30
  self.validate_database_copy_section,
31
31
  self.validate_database_migration_input_sources,
32
+ self.validate_cache_invalidation_config,
32
33
  ]
33
34
  self.io = io
34
35
  self.session = session
@@ -232,3 +233,25 @@ class ConfigValidator:
232
233
  )
233
234
  if errors:
234
235
  raise ConfigValidatorError("\n".join(errors))
236
+
237
+ def validate_cache_invalidation_config(self, config: dict):
238
+ codebase_pipelines = config.get("codebase_pipelines")
239
+ if not codebase_pipelines:
240
+ return
241
+
242
+ errors = []
243
+
244
+ all_environments = [env for env in config.get("environments", {}).keys() if not env == "*"]
245
+
246
+ for codebase in codebase_pipelines.values():
247
+ cache_invalidation_config = codebase.get("cache_invalidation")
248
+ if cache_invalidation_config:
249
+ for domain, config in cache_invalidation_config.get("domains").items():
250
+ environment = config.get("environment")
251
+ if environment not in all_environments:
252
+ errors.append(
253
+ f"Error in cache invalidation configuration for the domain '{domain}'. Environment '{environment}' is not defined for this application"
254
+ )
255
+
256
+ if errors:
257
+ raise ConfigValidatorError("\n".join(errors))
@@ -7,12 +7,12 @@ from dbt_platform_helper.platform_exception import PlatformException
7
7
  class EnvironmentVariableProvider:
8
8
 
9
9
  @staticmethod
10
- def get(env_var: str) -> Optional[str]:
10
+ def get(env_var: str, default=None) -> Optional[str]:
11
11
  """Returns the stripped value or None if not set or empty."""
12
12
  value = os.environ.get(env_var)
13
13
  if value and value.strip():
14
14
  return value.strip()
15
- return None
15
+ return default
16
16
 
17
17
  @staticmethod
18
18
  def get_required(env_var: str) -> str:
@@ -149,8 +149,9 @@ class LoadBalancerProvider:
149
149
 
150
150
  for rule in rules:
151
151
  for action in rule["Actions"]:
152
- if action["Type"] == "forward" and action["TargetGroupArn"] == target_group_arn:
153
- conditions = rule["Conditions"]
152
+ if "TargetGroupArn" in action:
153
+ if action["Type"] == "forward" and action["TargetGroupArn"] == target_group_arn:
154
+ conditions = rule["Conditions"]
154
155
 
155
156
  if not conditions:
156
157
  raise ListenerRuleConditionsNotFoundException(listener_arn)
@@ -17,6 +17,68 @@ class TerraformManifestProvider:
17
17
  self.file_provider = file_provider
18
18
  self.io = io
19
19
 
20
+ def generate_service_config(
21
+ self,
22
+ config_object,
23
+ environment,
24
+ image_tag,
25
+ platform_helper_version: str,
26
+ platform_config,
27
+ module_source_override: str = None,
28
+ ):
29
+
30
+ service_dir = f"terraform/services/{environment}/{config_object.name}"
31
+ platform_config = ConfigProvider.apply_environment_defaults(platform_config)
32
+ account = self._get_account_for_env(environment, platform_config)
33
+ state_key_suffix = f"{config_object.name}-{environment}"
34
+
35
+ terraform = {}
36
+ self._add_header(terraform)
37
+
38
+ self._add_service_locals(terraform, environment, image_tag)
39
+
40
+ self._add_provider(terraform, account)
41
+ self._add_backend(
42
+ terraform, platform_config, account, f"tfstate/services/{state_key_suffix}.tfstate"
43
+ )
44
+
45
+ self._add_service_module(terraform, platform_helper_version, module_source_override)
46
+
47
+ self._write_terraform_json(terraform, service_dir)
48
+
49
+ def _add_service_locals(self, terraform, environment, image_tag):
50
+ terraform["locals"] = {
51
+ "environment": environment,
52
+ "image_tag": image_tag,
53
+ "platform_config": '${yamldecode(file("../../../../platform-config.yml"))}',
54
+ "application": '${local.platform_config["application"]}',
55
+ "environments": '${local.platform_config["environments"]}',
56
+ "env_config": '${{for name, config in local.environments: name => merge(lookup(local.environments, "*", {}), config)}}',
57
+ "service_config": '${yamldecode(templatefile("./service-config.yml", {COPILOT_ENVIRONMENT_NAME = local.environment, IMAGE_TAG = local.image_tag}))}',
58
+ "raw_env_config": '${local.platform_config["environments"]}',
59
+ "combined_env_config": '${{for name, config in local.raw_env_config: name => merge(lookup(local.raw_env_config, "*", {}), config)}}',
60
+ "service_deployment_mode": '${lookup(local.combined_env_config[local.environment], "service-deployment-mode", "copilot")}',
61
+ "non_copilot_service_deployment_mode": '${local.service_deployment_mode == "dual-deploy-copilot-traffic" || local.service_deployment_mode == "dual-deploy-platform-traffic" || local.service_deployment_mode == "platform" ? 1 : 0}',
62
+ }
63
+
64
+ def _add_service_module(
65
+ self, terraform: dict, platform_helper_version: str, module_source_override: str = None
66
+ ):
67
+ source = (
68
+ module_source_override
69
+ or f"git::git@github.com:uktrade/platform-tools.git//terraform/ecs-service?depth=1&ref={platform_helper_version}"
70
+ )
71
+ terraform["module"] = {
72
+ "ecs-service": {
73
+ "source": source,
74
+ "count": "${local.non_copilot_service_deployment_mode}",
75
+ "application": "${local.application}",
76
+ "environment": "${local.environment}",
77
+ "service_config": "${local.service_config}",
78
+ "env_config": "${local.env_config}",
79
+ }
80
+ }
81
+
20
82
  def generate_codebase_pipeline_config(
21
83
  self,
22
84
  platform_config: dict,
@@ -32,7 +94,12 @@ class TerraformManifestProvider:
32
94
  self._add_header(terraform)
33
95
  self._add_codebase_pipeline_locals(terraform)
34
96
  self._add_provider(terraform, default_account)
35
- self._add_backend(terraform, platform_config, default_account, state_key_suffix)
97
+ self._add_backend(
98
+ terraform,
99
+ platform_config,
100
+ default_account,
101
+ f"tfstate/application/{state_key_suffix}.tfstate",
102
+ )
36
103
  self._add_codebase_pipeline_module(
37
104
  terraform, platform_helper_version, deploy_repository, module_source
38
105
  )
@@ -56,7 +123,9 @@ class TerraformManifestProvider:
56
123
  terraform = {}
57
124
  self._add_header(terraform)
58
125
  self._add_environment_locals(terraform, application_name)
59
- self._add_backend(terraform, platform_config, account, state_key_suffix)
126
+ self._add_backend(
127
+ terraform, platform_config, account, f"tfstate/application/{state_key_suffix}.tfstate"
128
+ )
60
129
  self._add_extensions_module(terraform, platform_helper_version, env, module_source_override)
61
130
  self._add_moved(terraform, platform_config)
62
131
  self._ensure_no_hcl_manifest_file(env_dir)
@@ -99,13 +168,13 @@ class TerraformManifestProvider:
99
168
  terraform["provider"]["aws"]["shared_credentials_files"] = ["~/.aws/config"]
100
169
 
101
170
  @staticmethod
102
- def _add_backend(terraform: dict, platform_config: dict, account: str, state_key_suffix: str):
171
+ def _add_backend(terraform: dict, platform_config: dict, account: str, state_key: str):
103
172
  terraform["terraform"] = {
104
173
  "required_version": SUPPORTED_TERRAFORM_VERSION,
105
174
  "backend": {
106
175
  "s3": {
107
176
  "bucket": f"terraform-platform-state-{account}",
108
- "key": f"tfstate/application/{state_key_suffix}.tfstate",
177
+ "key": state_key,
109
178
  "region": "eu-west-2",
110
179
  "encrypt": True,
111
180
  "kms_key_id": f"alias/terraform-platform-state-s3-key-{account}",
@@ -135,6 +204,7 @@ class TerraformManifestProvider:
135
204
  "deploy_repository": f"{deploy_repository}",
136
205
  "deploy_repository_branch": '${lookup(each.value, "deploy_repository_branch", "main")}',
137
206
  "additional_ecr_repository": '${lookup(each.value, "additional_ecr_repository", null)}',
207
+ "cache_invalidation": '${lookup(each.value, "cache_invalidation", null)}',
138
208
  "pipelines": '${lookup(each.value, "pipelines", [])}',
139
209
  "services": "${each.value.services}",
140
210
  "requires_image_build": '${lookup(each.value, "requires_image_build", true)}',
@@ -24,3 +24,8 @@
24
24
  Condition:
25
25
  StringEquals:
26
26
  'ssm:ResourceTag/copilot-application': '__all__'
27
+
28
+ - op: add
29
+ path: /Resources/TaskDefinition/Properties/pidMode
30
+ value:
31
+ task
@@ -0,0 +1,10 @@
1
+ def deep_merge(base, override):
2
+ result = base.copy()
3
+
4
+ for k, v in override.items():
5
+ if k in result and isinstance(result[k], dict) and isinstance(v, dict):
6
+ result[k] = deep_merge(result[k], v)
7
+ else:
8
+ result[k] = v
9
+
10
+ return result
@@ -1,11 +1,11 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: dbt-platform-helper
3
- Version: 15.6.0
3
+ Version: 15.8.0
4
4
  Summary: Set of tools to help transfer applications/services from GOV.UK PaaS to DBT PaaS augmenting AWS Copilot.
5
5
  License: MIT
6
6
  Author: Department for Business and Trade Platform Team
7
7
  Author-email: sre-team@digital.trade.gov.uk
8
- Requires-Python: >=3.9, !=2.7.*, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*, !=3.6.*, !=3.7.*, !=3.8.*
8
+ Requires-Python: >3.9.1,<4.0
9
9
  Classifier: License :: OSI Approved :: MIT License
10
10
  Classifier: Programming Language :: Python :: 3
11
11
  Classifier: Programming Language :: Python :: 3.10
@@ -13,7 +13,7 @@ Classifier: Programming Language :: Python :: 3.11
13
13
  Classifier: Programming Language :: Python :: 3.12
14
14
  Classifier: Programming Language :: Python :: 3.13
15
15
  Requires-Dist: Jinja2 (==3.1.6)
16
- Requires-Dist: PyYAML (==6.0.1)
16
+ Requires-Dist: PyYAML (==6.0.2)
17
17
  Requires-Dist: aiohttp (>=3.11.16,<4.0.0)
18
18
  Requires-Dist: boto3 (>=1.35.2,<2.0.0)
19
19
  Requires-Dist: boto3-stubs (>=1.26.148,<2.0.0)
@@ -23,7 +23,6 @@ Requires-Dist: cfn-flip (==1.3.0)
23
23
  Requires-Dist: cfn-lint (>=1.4.2,<2.0.0)
24
24
  Requires-Dist: checkov (>=3.2.405,<4.0.0)
25
25
  Requires-Dist: click (>=8.1.3,<9.0.0)
26
- Requires-Dist: cloudfoundry-client (==1.35.2)
27
26
  Requires-Dist: cryptography (>=44.0.1,<45)
28
27
  Requires-Dist: jinja2-simple-tags (>=0.5.0,<0.6.0)
29
28
  Requires-Dist: jsonschema (>=4.17.0,<4.18.0)
@@ -34,7 +33,7 @@ Requires-Dist: requests (>=2.31.0,<3.0.0)
34
33
  Requires-Dist: schema (==0.7.5)
35
34
  Requires-Dist: semver (>=3.0.2,<4.0.0)
36
35
  Requires-Dist: slack-sdk (>=3.27.1,<4.0.0)
37
- Requires-Dist: tomlkit (>=0.12.2,<0.13.0)
36
+ Requires-Dist: tomlkit (>=0.12.2,<0.14.0)
38
37
  Requires-Dist: yamllint (>=1.35.1,<2.0.0)
39
38
  Description-Content-Type: text/markdown
40
39
 
@@ -1,8 +1,8 @@
1
- dbt_platform_helper/COMMANDS.md,sha256=szFwoNuKlrTfGv10jA0zLG_HDgRnzPaI4rBBSgFXtu8,23883
1
+ dbt_platform_helper/COMMANDS.md,sha256=eR_fK_iMO59_PjGWZIv3MDD61zG5Hlp9hbHeoXaHwyY,25096
2
2
  dbt_platform_helper/README.md,sha256=B0qN2_u_ASqqgkGDWY2iwNGZt_9tUgMb9XqtaTuzYjw,1530
3
3
  dbt_platform_helper/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
4
  dbt_platform_helper/commands/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
5
- dbt_platform_helper/commands/application.py,sha256=OUQsahXXHSEKxmXAmK8fSy_bTLNwM_TdLuv6CvffRPk,10126
5
+ dbt_platform_helper/commands/application.py,sha256=GefE6MHuIdERUvd39K_3a-2v6VZUFQIS6F1U0GMXdAc,10156
6
6
  dbt_platform_helper/commands/codebase.py,sha256=oNlZcP2w3XE5YP-JVl0rdqoJuXUrfe1ELZ5xAdgPvBk,3166
7
7
  dbt_platform_helper/commands/conduit.py,sha256=GVDX2QjeGCxgN0otWOPivo0jyoE0CjnBz3VCDx9s-po,2313
8
8
  dbt_platform_helper/commands/config.py,sha256=4pgGgaH7Mp93UWdVoqZDc2eAmjsP7Yw1jjsviNWXsks,1442
@@ -12,9 +12,10 @@ dbt_platform_helper/commands/environment.py,sha256=zexiry6_dbOmD8w2lBrgdcFQKAG7X
12
12
  dbt_platform_helper/commands/generate.py,sha256=4M0ZiGN2w-bwgPMxeItFfT6vA7sOMjuceHEN7RAYkhc,735
13
13
  dbt_platform_helper/commands/notify.py,sha256=DMXA0AHot6-7CMzmY0PXPMMBVnLgvQgXr6np6OnSQh4,3401
14
14
  dbt_platform_helper/commands/pipeline.py,sha256=PGpDDmyReVa4gdpXDwJEsHN51f5MgTIbm2AibTkuWrE,2580
15
- dbt_platform_helper/commands/secrets.py,sha256=haSbF401H4XLzS9LWGLC6h_EcZHLcIF-rW1XkzUOy58,3985
15
+ dbt_platform_helper/commands/secrets.py,sha256=AMwLCi7uxuMMfhDfGD8OcuIYMzU8q2VQXWxaiuztvzA,3296
16
+ dbt_platform_helper/commands/service.py,sha256=Lqo4vmNHxa0ofTWIYEpwKoyT1iqsTCkMjz3HeqSE4og,1702
16
17
  dbt_platform_helper/commands/version.py,sha256=2GltWeeN7cqhVj9FhYWSbXSQSyNILHVNOeANGWtAllY,1097
17
- dbt_platform_helper/constants.py,sha256=m5IkYADa4icxfIAK-hKCIoKhyi0FvoL-URAzomyE5lg,1507
18
+ dbt_platform_helper/constants.py,sha256=q_g7kJrOGsXql2GHa3yU9Sx-qQmUqg2oi8SWpxGaX-o,1857
18
19
  dbt_platform_helper/default-extensions.yml,sha256=SU1ZitskbuEBpvE7efc3s56eAUF11j70brhj_XrNMMo,493
19
20
  dbt_platform_helper/domain/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
20
21
  dbt_platform_helper/domain/codebase.py,sha256=2hJoBiDB2ciOudT_YUR44XV0ZQPWUJld_UIuds4XOt8,12481
@@ -27,10 +28,12 @@ dbt_platform_helper/domain/maintenance_page.py,sha256=0_dgM5uZvjVNBKcqScspjutinM
27
28
  dbt_platform_helper/domain/notify.py,sha256=_BWj5znDWtrSdJ5xzDBgnao4ukliBA5wiUZGobIDyiI,1894
28
29
  dbt_platform_helper/domain/pipelines.py,sha256=rL_NArksFgmpsIUL3K_xVmTWt10tmlw5eCP140j96bc,7832
29
30
  dbt_platform_helper/domain/plans.py,sha256=X5-jKGiJDVWn0CRH1k5aV74fTH0E41HqFQcCo5kB4hI,1160
31
+ dbt_platform_helper/domain/service.py,sha256=b1wtvINDhyMmauTb4Ed2wyyGQXsKPfB-SIcewjXiCwA,6771
30
32
  dbt_platform_helper/domain/terraform_environment.py,sha256=g9PSuZeVXgboGNDXU7OGMiRATd67NU8S92HUGri1fbo,2471
31
33
  dbt_platform_helper/domain/versioning.py,sha256=pIL8VPAJHqX5kJBp3QIxII5vmUo4aIYW_U9u_KxUJd0,5494
32
- dbt_platform_helper/entities/platform_config_schema.py,sha256=1T9tKqDV4nsyr-UYhqg6NqRBUF6xgF7SPpdomGGjtMo,26931
34
+ dbt_platform_helper/entities/platform_config_schema.py,sha256=IUwjxQ19SotiAHLe5K9lpftaw4tJmMk-U6PQbe-HzEU,27551
33
35
  dbt_platform_helper/entities/semantic_version.py,sha256=VgQ6V6OgSaleuVmMB8Kl_yLoakXl2auapJTDbK00mfc,2679
36
+ dbt_platform_helper/entities/service.py,sha256=9EkcSBVGxiLunyd_WneanW0_dbwX7QcF_YWjBvR_k_4,4147
34
37
  dbt_platform_helper/jinja2_tags.py,sha256=hKG6RS3zlxJHQ-Op9r2U2-MhWp4s3lZir4Ihe24ApJ0,540
35
38
  dbt_platform_helper/platform_exception.py,sha256=HGfCYRD20REsynqMKmyZndTfdkMd5dLSIEB2qGGCeP8,244
36
39
  dbt_platform_helper/providers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -42,23 +45,23 @@ dbt_platform_helper/providers/aws/redis.py,sha256=i3Kb00_BdqssjQg1wgZ-8GRXcEWQiO
42
45
  dbt_platform_helper/providers/aws/sso_auth.py,sha256=1cE9gVu0XZoE3Nycs1anShistRU_CZCOGMFzFpaAq0w,2275
43
46
  dbt_platform_helper/providers/cache.py,sha256=1hEwp0y9WYbEfgsp-RU9MyzIgCt1-4BxApgd_0uVweE,3615
44
47
  dbt_platform_helper/providers/cloudformation.py,sha256=syMH6xc-ALRbsYQvlw9RcjX7c1MufFzwEdEzp_ucWig,5359
45
- dbt_platform_helper/providers/config.py,sha256=8eK6txDTTF3s3iy7WxKszlaE33pHVhbJ55UDUJ9_nYw,9861
46
- dbt_platform_helper/providers/config_validator.py,sha256=uF1GB-fl0ZuXVCtLNANgnY22UbiWZniBg1PiXgzGzuU,9923
48
+ dbt_platform_helper/providers/config.py,sha256=652JeXsZgRcV2vN-mKDh_FwapaGdL4dOkHx8mRRohG4,10993
49
+ dbt_platform_helper/providers/config_validator.py,sha256=kZZAJIApKKZ9yjDwCnZ6oL6ZSfaA-CxQrisnAWNgIhw,10966
47
50
  dbt_platform_helper/providers/copilot.py,sha256=voFVGhvtOElulx6Cgd1KQGkybrg8v4oGkJTr_xRpF18,5582
48
51
  dbt_platform_helper/providers/ecr.py,sha256=eYXSY1-pFN6F3Es1WSZgv3dmvX2oD-baqhHDO-QzgVg,4382
49
52
  dbt_platform_helper/providers/ecs.py,sha256=4XRpOgcl7KFiTp9lhNrp4Lvmje0ZFYuUh9Z_eEqhyhA,6538
50
- dbt_platform_helper/providers/environment_variable.py,sha256=0v1thpqhsYHwLvzSc7eagV9KiwFQ_7cIdjl-KB2NUNM,777
53
+ dbt_platform_helper/providers/environment_variable.py,sha256=PopMLKnWcoObQrgg7xSEb-pzVT54_QGFsb_HtL_9YU8,794
51
54
  dbt_platform_helper/providers/files.py,sha256=sH02Ka_WaDBFtN4Y0i9kuo9YQoKn2rhUy0Dn8LPMrEw,857
52
55
  dbt_platform_helper/providers/io.py,sha256=-C_NLHH_ebN2G5ZMtAAj4E9AqcDZX98mIWgAV34Dm7U,1737
53
56
  dbt_platform_helper/providers/kms.py,sha256=JR2EU3icXePoJCtr7QnqDPj1wWbyn5Uf9CRFq3_4lRs,647
54
- dbt_platform_helper/providers/load_balancers.py,sha256=DqmUeFyfrHGzZiR16SCXqZ6q1mjrWtUWFCdXfcb2kgw,11745
57
+ dbt_platform_helper/providers/load_balancers.py,sha256=Xm7cyFIKfGPeD1HzIFCT8XQ6QJA8yJOwZjTuc6k8zcw,11800
55
58
  dbt_platform_helper/providers/parameter_store.py,sha256=klxDhcQ65Yc2KAc4Gf5P0vhpZOW7_vZalAVb-LLAA4s,1568
56
59
  dbt_platform_helper/providers/schema_migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
57
60
  dbt_platform_helper/providers/schema_migrations/schema_v0_to_v1_migration.py,sha256=dplbvEAc8l8F4dEAy3JwLP1Phjkt4QVuQYNX_EKe_Ls,2036
58
61
  dbt_platform_helper/providers/schema_migrator.py,sha256=qk14k3hMz1av9VrxHyJw2OKJLQnCBv_ugOoxZr3tFXQ,2854
59
62
  dbt_platform_helper/providers/secrets.py,sha256=mOTIrcRRxxV2tS40U8onAjWekfPS3NzCvvyCMjr_yrU,5327
60
63
  dbt_platform_helper/providers/slack_channel_notifier.py,sha256=G8etEcaBQSNHg8BnyC5UPv6l3vUB14cYWjcaAQksaEk,2135
61
- dbt_platform_helper/providers/terraform_manifest.py,sha256=Qst0GwLyOgHgmli3gVciVXCCEWEyfTJEle-AfmCfF34,9899
64
+ dbt_platform_helper/providers/terraform_manifest.py,sha256=_q8Lk_9TcgD0cMg2_kpeWxYcf00kCgT00FcHg8-zG4Y,13135
62
65
  dbt_platform_helper/providers/validation.py,sha256=i2g-Mrd4hy_fGIfGa6ZQy4vTJ40OM44Fe_XpEifGWxs,126
63
66
  dbt_platform_helper/providers/version.py,sha256=QNGrV5nyJi0JysXowYUU4OrXGDn27WmFezlV8benpdY,4251
64
67
  dbt_platform_helper/providers/version_status.py,sha256=qafnhZrEc9k1cvXJpvJhkGj6WtkzcsoQhqS_Y6JXy48,929
@@ -87,23 +90,24 @@ dbt_platform_helper/templates/environment-pipelines/main.tf,sha256=yrbxIhHtWOm7g
87
90
  dbt_platform_helper/templates/svc/maintenance_pages/default.html,sha256=OTZ-qwwSXu7PFbsgp4kppdm1lsg_iHK7FCFqhPnvrEs,741
88
91
  dbt_platform_helper/templates/svc/maintenance_pages/dmas-migration.html,sha256=qvI6tHuI0UQbMBCuvPgK1a_zLANB6w7KVo9N5d8r-i0,829
89
92
  dbt_platform_helper/templates/svc/maintenance_pages/migration.html,sha256=GiQsOiuaMFb7jG5_wU3V7BMcByHBl9fOBgrNf8quYlw,783
90
- dbt_platform_helper/templates/svc/overrides/cfn.patches.yml,sha256=W7-d017akuUq9kda64DQxazavcRcCPDjaAik6t1EZqM,742
93
+ dbt_platform_helper/templates/svc/overrides/cfn.patches.yml,sha256=EdzEo-utytsye4Ck3bVSK8SuKdwUhX7HtQGdvTvHfps,824
91
94
  dbt_platform_helper/utilities/decorators.py,sha256=rS6ohsuo0bc6fkZP98Qwaeh0c_v2MDqn9hCvqfoz2w8,3548
92
95
  dbt_platform_helper/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
93
96
  dbt_platform_helper/utils/application.py,sha256=414czQmCUMDohoz38cY8FjoitJolCmRxCqc9TMJTsE4,6951
94
97
  dbt_platform_helper/utils/arn_parser.py,sha256=BaXzIxSOLdFmP_IfAxRq-0j-0Re1iCN7L4j2Zi5-CRQ,1304
95
98
  dbt_platform_helper/utils/aws.py,sha256=mnffVJzvXojfmYh1waeUWwHYo24jRu95rXg8LiuogtA,13096
96
99
  dbt_platform_helper/utils/click.py,sha256=Fx4y4bbve1zypvog_sgK7tJtCocmzheoEFLBRv1lfdM,2943
100
+ dbt_platform_helper/utils/deep_merge.py,sha256=2N0Gg2SsFHzwyNNLFm2Q8d4C9PPFsDsaWYzjDO1OpaI,280
97
101
  dbt_platform_helper/utils/git.py,sha256=9jyLhv37KKE6r-_hb3zvjhTbluA81kdrOdNeG6MB-_M,384
98
102
  dbt_platform_helper/utils/messages.py,sha256=nWA7BWLb7ND0WH5TejDN4OQUJSKYBxU4tyCzteCrfT0,142
99
103
  dbt_platform_helper/utils/template.py,sha256=g-Db-0I6a6diOHkgK1nYA0IxJSO4TRrjqOvlyeOR32o,950
100
104
  dbt_platform_helper/utils/validation.py,sha256=W5jKC2zp5Q7cJ0PT57GB-s9FkJXrNt1jmWojXRFymcY,1187
101
- platform_helper.py,sha256=_YNNGtMkH5BcpC_mQQYJrmlf2mt7lkxTYeH7ZgflPoA,1925
105
+ platform_helper.py,sha256=LP9o5_1H9-FKbBa85FqW07QFPn6STg6D4gM2-QHaaD4,2048
102
106
  terraform/elasticache-redis/plans.yml,sha256=efJfkLuLC_5TwhLb9DalKHOuZFO79y6iei6Dg_tqKjI,1831
103
107
  terraform/opensearch/plans.yml,sha256=lQbUSNMGfvUeDMcGx8mSwzGQhMJU3EZ4J4tPzPKaq6c,1471
104
108
  terraform/postgres/plans.yml,sha256=plwCklW1VB_tNJFyUduRMZx9UANgiWH_7TGLWUaUEus,2553
105
- dbt_platform_helper-15.6.0.dist-info/LICENSE,sha256=dP79lN73--7LMApnankTGLqDbImXg8iYFqWgnExGkGk,1090
106
- dbt_platform_helper-15.6.0.dist-info/METADATA,sha256=t3DF_0fn1sT4hseK6F1MZ4H-GaMClJKiU2RaS5ZK_Gc,3293
107
- dbt_platform_helper-15.6.0.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
108
- dbt_platform_helper-15.6.0.dist-info/entry_points.txt,sha256=QhbY8F434A-onsg0-FsdMd2U6HKh6Q7yCFFZrGUh5-M,67
109
- dbt_platform_helper-15.6.0.dist-info/RECORD,,
109
+ dbt_platform_helper-15.8.0.dist-info/LICENSE,sha256=dP79lN73--7LMApnankTGLqDbImXg8iYFqWgnExGkGk,1090
110
+ dbt_platform_helper-15.8.0.dist-info/METADATA,sha256=SC2tYGy-f0qweGjG1PxdtYALvhi-FnzEJB6IQ2vZIr8,3163
111
+ dbt_platform_helper-15.8.0.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
112
+ dbt_platform_helper-15.8.0.dist-info/entry_points.txt,sha256=QhbY8F434A-onsg0-FsdMd2U6HKh6Q7yCFFZrGUh5-M,67
113
+ dbt_platform_helper-15.8.0.dist-info/RECORD,,
platform_helper.py CHANGED
@@ -15,6 +15,7 @@ from dbt_platform_helper.commands.generate import generate as generate_commands
15
15
  from dbt_platform_helper.commands.notify import notify as notify_commands
16
16
  from dbt_platform_helper.commands.pipeline import pipeline as pipeline_commands
17
17
  from dbt_platform_helper.commands.secrets import secrets as secrets_commands
18
+ from dbt_platform_helper.commands.service import service as service_commands
18
19
  from dbt_platform_helper.commands.version import version as version_commands
19
20
  from dbt_platform_helper.utils.click import ClickDocOptGroup
20
21
 
@@ -37,6 +38,7 @@ platform_helper.add_command(environment_commands)
37
38
  platform_helper.add_command(generate_commands)
38
39
  platform_helper.add_command(pipeline_commands)
39
40
  platform_helper.add_command(secrets_commands)
41
+ platform_helper.add_command(service_commands)
40
42
  platform_helper.add_command(notify_commands)
41
43
  platform_helper.add_command(database_commands)
42
44
  platform_helper.add_command(version_commands)