Source code for ecs_composex.s3.s3_stack

# SPDX-License-Identifier: MPL-2.0
# Copyright 2020-2022 John Mille <john@compose-x.io>

"""
Module to control S3 stack
"""
from __future__ import annotations

from typing import TYPE_CHECKING

if TYPE_CHECKING:
    from ecs_composex.common.settings import ComposeXSettings
    from ecs_composex.mods_manager import XResourceModule

from botocore.exceptions import ClientError
from compose_x_common.aws.s3 import S3_BUCKET_ARN_RE
from compose_x_common.compose_x_common import attributes_to_mapping, keyisset
from troposphere.s3 import Bucket as CfnBucket

from ecs_composex.common.logging import LOG
from ecs_composex.common.stacks import ComposeXStack
from ecs_composex.common.troposphere_tools import build_template
from ecs_composex.s3.s3_bucket import Bucket
from ecs_composex.s3.s3_params import (
    CONTROL_CLOUD_ATTR_MAPPING,
    S3_BUCKET_ARN,
    S3_BUCKET_KMS_KEY,
    S3_BUCKET_KMS_KEY_ARN,
    S3_BUCKET_NAME,
)
from ecs_composex.s3.s3_template import create_s3_template


[docs]def get_bucket_config(bucket: Bucket, resource_id: str) -> dict: """ :param ecs_composex.s3.s3_bucket.Bucket bucket: :param str resource_id: """ bucket_config = { S3_BUCKET_NAME: resource_id, S3_BUCKET_ARN: bucket.arn, } client = bucket.lookup_session.client("s3") try: encryption_r = client.get_bucket_encryption(Bucket=resource_id) encryption_attributes = attributes_to_mapping( encryption_r, CONTROL_CLOUD_ATTR_MAPPING ) if keyisset( CONTROL_CLOUD_ATTR_MAPPING[S3_BUCKET_KMS_KEY], encryption_attributes, ): bucket_config[S3_BUCKET_KMS_KEY] = encryption_attributes[S3_BUCKET_KMS_KEY] except ClientError as error: if ( not error.response["Error"]["Code"] == "ServerSideEncryptionConfigurationNotFoundError" ): raise LOG.warning(error.response["Error"]["Message"]) return bucket_config
[docs]def define_bucket_mappings( lookup_buckets: list[Bucket], settings: ComposeXSettings, module: XResourceModule ) -> None: """ Method to define CFN Mappings for the lookup buckets :param list[Bucket] lookup_buckets: :param ComposeXSettings settings: :param module: """ for bucket in lookup_buckets: bucket.init_outputs() bucket.lookup_resource( S3_BUCKET_ARN_RE, get_bucket_config, CfnBucket.resource_type, "s3" ) settings.mappings[module.mapping_key].update( {bucket.logical_name: bucket.mappings} ) if not keyisset(S3_BUCKET_KMS_KEY, bucket.lookup_properties): LOG.info( f"{module.res_key}.{bucket.name} - No CMK Key identified. Not KMS permissions to set." ) else: LOG.info( f"{module.res_key}.{bucket.name} - " f"CMK identified {bucket.lookup_properties[S3_BUCKET_KMS_KEY]}." ) key_arn_r = bucket.lookup_session.client("kms").describe_key( KeyId=bucket.lookup_properties[S3_BUCKET_KMS_KEY] )["KeyMetadata"]["Arn"] bucket.lookup_properties.update({S3_BUCKET_KMS_KEY_ARN: key_arn_r}) LOG.info( f"{module.res_key}.{bucket.name} - " f"CMK ARN - {bucket.lookup_properties[S3_BUCKET_KMS_KEY_ARN]}" ) bucket.add_new_output_attribute( S3_BUCKET_KMS_KEY_ARN, ( f"{bucket.logical_name}{S3_BUCKET_KMS_KEY_ARN.return_value}", None, None, S3_BUCKET_KMS_KEY_ARN.return_value, ), ) bucket.add_new_output_attribute( S3_BUCKET_KMS_KEY, ( f"{bucket.logical_name}{S3_BUCKET_KMS_KEY.return_value}", None, None, S3_BUCKET_KMS_KEY.return_value, ), ) bucket.generate_cfn_mappings_from_lookup_properties() bucket.generate_outputs()
[docs]class XStack(ComposeXStack): """ Class to handle S3 buckets """ def __init__( self, title: str, settings: ComposeXSettings, module: XResourceModule, **kwargs ): if module.lookup_resources: if not keyisset(module.mapping_key, settings.mappings): settings.mappings[module.mapping_key] = {} define_bucket_mappings(module.lookup_resources, settings, module) if module.new_resources: stack_template = build_template(f"Root template for {settings.name}.s3") super().__init__(module.mapping_key, stack_template, **kwargs) create_s3_template(module.new_resources, stack_template) self.parent_stack = settings.root_stack if not hasattr(self, "DeletionPolicy"): setattr(self, "DeletionPolicy", module.module_deletion_policy) else: self.is_void = True for resource in module.resources_list: resource.stack = self