Exemplo n.º 1
0
 def validate(self, settings):
     """
     Method to validate the CloudFormation template, either via URL once uploaded to S3 or via TemplateBody
     """
     try:
         if not settings.no_upload and self.url:
             validate_wrapper(settings.session, url=self.url)
         elif settings.no_upload or not self.url:
             if not self.file_path:
                 self.write(settings)
             LOG.debug(f"No upload - Validating template body - {self.file_path}")
             if len(self.body) >= 51200:
                 LOG.warning(
                     f"Template body for {self.file_name} is too big for local validation."
                     " No upload is True, so skipping."
                 )
             else:
                 validate_wrapper(settings.session, body=self.body)
         LOG.debug(f"Template {self.file_name} was validated successfully by CFN")
     except ClientError as error:
         LOG.error(error)
         with open(f"/tmp/{settings.name}.{settings.format}", "w") as failed_file_fd:
             failed_file_fd.write(self.body)
             LOG.error(
                 f"Failed validation template written at /tmp/{settings.name}.{settings.format}"
             )
             raise
Exemplo n.º 2
0
def find_aws_resource_arn_from_tags_api(
    info, session, aws_resource_search, types=None, allow_multi=False
):
    """
    Function to find the RDS DB based on info

    :param dict info:
    :param boto3.session.Session session: Boto3 session for clients
    :param str aws_resource_search: Resource type we are after within the AWS Service, ie. cluster, instance
    :param dict types: Additional types to match.
    :return:
    """
    res_types = deepcopy(ARNS_PER_TAGGINGAPI_TYPE)
    if types is not None and isinstance(types, dict):
        res_types.update(types)
    search_tags = (
        define_tagsgroups_filter_tags(info["Tags"]) if keyisset("Tags", info) else ()
    )
    name = info["Name"] if keyisset("Name", info) else None

    resources_r = get_resources_from_tags(session, aws_resource_search, search_tags)
    LOG.debug(search_tags)
    if not resources_r or not keyisset("ResourceTagMappingList", resources_r):
        arns = []
    else:
        arns = [i["ResourceARN"] for i in resources_r["ResourceTagMappingList"]]
    return handle_search_results(
        arns, name, res_types, aws_resource_search, allow_multi=allow_multi
    )
Exemplo n.º 3
0
    def handle_x_dependencies(
        self, settings: ComposeXSettings, root_stack: ComposeXStack
    ) -> None:
        """

        :param settings:
        :param root_stack:
        :return:
        """
        handle_ecs_cluster(settings, bucket=self)
        for resource in settings.get_x_resources(include_mappings=False):
            if not resource.cfn_resource:
                continue
            if not resource.stack:
                LOG.debug(
                    f"resource {resource.name} has no `stack` attribute defined. Skipping"
                )
                continue
            mappings = [(DeliveryStream, s3_to_firehose)]
            for target in mappings:
                if isinstance(resource, target[0]) or issubclass(
                    type(resource), target[0]
                ):
                    target[1](
                        self,
                        resource,
                        resource.stack,
                        settings,
                    )
Exemplo n.º 4
0
    def set_services_targets_from_dict(self, settings):
        """
        Deals with services set as a dict

        :param settings:
        :return:
        """
        for service_name, service_def in self.services.items():
            if service_name in settings.families and service_name not in [
                    f[0].name for f in self.families_targets
            ]:
                self.families_targets.append((
                    settings.families[service_name],
                    True,
                    settings.families[service_name].services,
                    service_def["Access"]
                    if keyisset("Access", service_def) else {},
                    service_def,
                ))
            elif service_name in settings.families and service_name in [
                    f[0].name for f in self.families_targets
            ]:
                LOG.debug(
                    f"{self.module.res_key}.{self.name} - Family {service_name} has already been added. Skipping"
                )
            elif service_name in [s.name for s in settings.services]:
                self.handle_families_targets_expansion_dict(
                    service_name, service_def, settings)
Exemplo n.º 5
0
    def set_services_targets_scaling_from_dict(self, settings) -> None:
        """
        Deals with services set as a dict

        :param settings:
        """
        for service_name, service_def in self.services.items():
            if not keyisset("Scaling", service_def):
                LOG.debug(
                    f"{self.module.res_key}.{self.name} - No Scaling set for {service_name}"
                )
                continue
            if service_name in settings.families and service_name not in [
                    f[0].name for f in self.families_scaling
            ]:
                self.families_scaling.append((
                    settings.families[service_name],
                    service_def["Scaling"],
                ))
            elif service_name in settings.families and service_name in [
                    f[0].name for f in self.families_scaling
            ]:
                LOG.debug(
                    f"{self.module.res_key}.{self.name} - Family {service_name} has already been added. Skipping"
                )
            elif service_name in [s.name for s in settings.services]:
                self.handle_families_scaling_expansion_dict(
                    service_name, service_def, settings)
    def set_ext_sources_ingress(self, destination_tile, security_group):
        """
        Method to add ingress rules from external sources to a given Security Group (ie. ALB Security Group).
        If a list of IPs is found in the config['ext_sources'] part of the network section of configs for the service,
        then it will use that. If no IPv4 source is indicated, it will by default allow traffic from 0.0.0.0/0

        :param str destination_tile: The name of the destination for description
        :param security_group: security group (object or title string) to add the rules to
        :type security_group: str or troposphere.ec2.SecurityGroup or troposphere.Ref or Troposphere.GetAtt
        """
        if not self.ext_sources:
            LOG.debug("No external rules defined. Skipping.")
            return

        for allowed_source in self.ext_sources:
            if not keyisset(self.ipv4_key, allowed_source) and not keyisset(
                    self.ipv6_key, allowed_source):
                LOG.warning(
                    f"No {self.ipv4_key} or {self.ipv6_key} set. Skipping")
                continue
            props = generate_security_group_props(allowed_source)
            if props:
                LOG.debug(f"Adding {allowed_source} for ingress")
                self.create_ext_sources_ingress_rule(destination_tile,
                                                     allowed_source,
                                                     security_group, **props)
Exemplo n.º 7
0
def generate_resource_permissions(
    resource_name, policies, arn, ignore_missing_primary=False
):
    """
    Function to generate IAM permissions for a given x-resource. Returns the mapping of these for the given resource.
    Suffix takes the values and reduces to the first 118 characters to ensure policy length is below 128
    Short prefix ensures the uniqueness of the policy name but allows to be a constant throughout the life
    of the CFN Stack. It is 8 chars long, leaving a 2 chars margin

    :param str resource_name: The name of the resource
    :param dict policies: the policies associated with the x-resource type.
    :param str,AWSHelper arn: The ARN of the resource if already looked up.
    :param bool ignore_missing_primary: Whether the policy should contain ${ARN} at least
    :return: dict of the IAM policies associated with the resource.
    :rtype dict:
    """
    resource_policies = {}
    for a_type in policies:
        clean_policy = {"Version": "2012-10-17", "Statement": []}
        LOG.debug(a_type)
        policy_doc = policies[a_type].copy()
        resources = determine_arns(arn, policy_doc, ignore_missing_primary)
        policy_doc["Sid"] = f"{a_type}To{resource_name}"
        policy_doc["Resource"] = resources
        clean_policy["Statement"].append(policy_doc)
        suffix = f"{a_type}{resource_name}"[:(118)]
        resource_policies[a_type] = IamPolicy(
            PolicyName=Sub(f"${{ID}}-{suffix}", ID=STACK_ID_SHORT),
            PolicyDocument=clean_policy,
        )
    return resource_policies
Exemplo n.º 8
0
    def __add__(self, other):
        """
        Function to merge two services config.
        """
        LOG.debug(f"Current LB: {self.lb_type}")
        if self.lb_type is None:
            if other.lb_type is not None:
                self.ports = other.ports
                self.lb_type = other.lb_type
                self.is_public = other.is_public

        elif self.lb_type is not None and other.lb_type is not None:
            if self.is_public:
                pass
            elif other.is_public and not self.is_public:
                self.ports = other.ports
                self.lb_type = other.lb_type
                self.lb_service_name = other.lb_service_name
                self.is_public = other.is_public
        LOG.debug(f"LB TYPE: {self.lb_type}")
        if other.use_xray or self.use_xray:
            self.use_xray = True
        if self.links or other.links:
            self.links += other.links
        return self
Exemplo n.º 9
0
def define_queue(queue_name, queue_def, queues, mono_template=True):
    """
    Function to parse the queue definition and generate the queue accordingly. Created the redrive policy if necessary

    :param str queue_name: name of the queue
    :param dict queue_def: queue definition as found in composex file
    :param dict queues: the queues defined in x-sqs
    :param bool mono_template: whether or not there are so many outputs we need to split.

    :return: queue
    :rtype: troposphere.sqs.Queue
    """
    redrive_policy = None
    if keypresent("Properties", queue_def):
        props = deepcopy(queue_def)
        properties = props["Properties"]
        properties.update({"Metadata": metadata})
    else:
        properties = {"Metadata": metadata}
    if keyisset("RedrivePolicy", properties) and keyisset(
            "deadLetterTargetArn", properties["RedrivePolicy"]):
        redrive_target = properties["RedrivePolicy"]["deadLetterTargetArn"]
        if redrive_target not in queues:
            raise KeyError(
                f"Queue {redrive_target} defined as DLQ for {queue_name} but is not defined"
            )
        if keyisset("maxReceiveCount", properties["RedrivePolicy"]):
            retries = int(properties["RedrivePolicy"]["maxReceiveCount"])
        else:
            retries = 5
        redrive_policy = define_redrive_policy(redrive_target, retries,
                                               mono_template)
    queue = set_queue(queue_name, properties, redrive_policy)
    LOG.debug(queue.title)
    return queue
Exemplo n.º 10
0
def get_key_config(key, account_id: str, resource_id: str) -> dict | None:
    """

    :param KmsKey key:
    :param str account_id: unused
    :param str resource_id: unused
    :return:
    """
    key_attributes_mappings = {
        KMS_KEY_ARN: "KeyMetadata::Arn",
        KMS_KEY_ID: "KeyMetadata::KeyId",
    }
    client = key.lookup_session.client("kms")
    try:
        key_desc = client.describe_key(KeyId=key.arn)
        key_attributes = attributes_to_mapping(key_desc,
                                               key_attributes_mappings)
        key.manager = key_desc["KeyMetadata"]["KeyManager"]
        try:
            aliases_r = client.list_aliases(KeyId=key_attributes[KMS_KEY_ID])
            if aliases_r["Aliases"]:
                key_attributes[KMS_KEY_ALIAS_NAME] = aliases_r["Aliases"][0][
                    "AliasName"]
        except client.exceptions.NotFoundException:
            LOG.debug(f"{key.module.res_key}.{key.name} - No KMS Key Alias.")
        return key_attributes
    except client.exceptions.QueueDoesNotExist:
        return None
    except ClientError as error:
        LOG.error(error)
        raise
Exemplo n.º 11
0
    def validate_schema(self,
                        name,
                        definition,
                        module_name,
                        module_schema: str = None) -> None:
        """
        JSON Validation of the resources module validation
        """
        if not self.module.json_schema and not module_schema:
            return
        resolver_source = pkg_files("ecs_composex").joinpath(
            "specs/compose-spec.json")
        LOG.debug(f"Validating against input schema {resolver_source}")
        resolver = jsonschema.RefResolver(
            base_uri=f"file://{path.abspath(path.dirname(resolver_source))}/",
            referrer=self.module.json_schema,
        )

        try:
            jsonschema.validate(
                definition,
                module_schema if module_schema else self.module.json_schema,
                resolver=resolver,
            )
        except jsonschema.exceptions.ValidationError:
            LOG.error(
                f"{module_name}.{name} - Definition is not conform to schema.")
            raise
Exemplo n.º 12
0
    def handle_x_dependencies(self, settings, root_stack=None) -> None:
        """
        WIll go over all the new resources to create in the execution and search for properties that can be updated
        with itself

        :param ecs_composex.common.settings.ComposeXSettings settings:
        :param ComposeXStack root_stack: Not used. Present for general compatibility
        """
        handle_ecs_cluster(settings, self)
        for resource in settings.get_x_resources(include_mappings=False):
            if not resource.cfn_resource:
                continue
            if not resource.stack:
                LOG.debug(
                    f"resource {resource.name} has no `stack` attribute defined. Skipping"
                )
                continue
            mappings = [
                (Bucket, handle_bucket_kms),
                (Queue, handle_queue_kms),
                (DeliveryStream, kms_to_firehose),
            ]
            for target in mappings:
                if isinstance(resource, target[0]) or issubclass(
                        type(resource), target[0]):
                    target[1](
                        self,
                        resource,
                        resource.stack,
                        settings,
                    )
Exemplo n.º 13
0
 def define_kms_key(self):
     """
     Method to set the KMS Key
     """
     if not self.properties:
         props = {
             "Description":
             Sub(
                 f"{self.name} created in ${{STACK_NAME}}",
                 STACK_NAME=define_stack_name(),
             ),
             "Enabled":
             True,
             "EnableKeyRotation":
             True,
             "KeyUsage":
             "ENCRYPT_DECRYPT",
             "PendingWindowInDays":
             7,
         }
     else:
         props = import_record_properties(self.properties, Key)
     if not keyisset("KeyPolicy", props):
         props.update({"KeyPolicy": define_default_key_policy()})
     props.update({"Metadata": metadata})
     LOG.debug(props)
     self.cfn_resource = Key(self.logical_name, **props)
Exemplo n.º 14
0
def merge_family_services_networking(family: ComposeFamily) -> dict:
    """
    Merge the different services network configuration definitions

    :param ecs_composex.ecs.ecs_family.ComposeFamily family:
    :return: The family network definition
    :rtype: dict
    """
    network_config = {
        Ingress.master_key: {
            "Myself": False,
            Ingress.ext_sources_key: [],
            Ingress.aws_sources_key: [],
            Ingress.services_key: [],
        },
        CLOUDMAP_KEY: {},
    }
    x_network_ingress = [
        s.x_network for s in family.ordered_services if s.x_network
    ]
    for network in x_network_ingress:
        for key, definition in network_config.items():
            merge_family_network_setting(family, key, definition, network,
                                         network_config)
    LOG.debug(family.name)
    LOG.debug(dumps(network_config, indent=2))
    return network_config
Exemplo n.º 15
0
    def render_parameters_list_cfn(self):
        """
        Renders parameters in a CFN parameters config file format

        :return: params
        :rtype: list
        """
        if not hasattr(self, "Parameters"):
            return []
        params = []
        for param_name in self.Parameters.keys():
            LOG.debug(f"{param_name} - {self.Parameters[param_name]}")
            if not isinstance(
                    self.Parameters[param_name],
                (Ref, GetAtt, ImportValue, If, Join, type(None)),
            ):
                if isinstance(self.Parameters[param_name], (int, str)):
                    params.append({
                        "ParameterKey":
                        param_name,
                        "ParameterValue":
                        self.Parameters[param_name],
                    })
                elif isinstance(self.Parameters[param_name], list):
                    params.append({
                        "ParameterKey":
                        param_name,
                        "ParameterValue":
                        ",".join(self.Parameters[param_name]),
                    })
        return params
Exemplo n.º 16
0
    def add_managed_sidecar(self, service: ComposeService):
        """
        Adds a new container/service to the Task Family and validates all settings that go along with the change.
        :param service:
        """

        if not isinstance(service, ComposeService) or not issubclass(
            type(service), ComposeService
        ):
            raise TypeError("service must be", ComposeService, "Got", type(service))
        if self.managed_sidecars and service.name in [
            svc.name for svc in self.managed_sidecars
        ]:
            LOG.debug(
                f"{self.name} - container service {service.name} is already set. Skipping"
            )
            return
        self.managed_sidecars.append(service)
        if self.task_definition and service.container_definition:
            self.task_definition.ContainerDefinitions.append(
                service.container_definition
            )
            self.set_secrets_access()
        self.iam_manager.init_update_policies()
        # self.handle_logging()

        self.service_compute.set_update_launch_type()
        self.service_compute.set_update_capacity_providers()
        self.task_compute.set_task_compute_parameter()
Exemplo n.º 17
0
def process_stacks(root_stack, settings, is_root=True):
    """
    Function to go through all stacks of a given template and update the template
    It will recursively render sub stacks defined.

    :param root_stack: the root template to iterate over the resources.
    :type root_stack: ecs_composex.common.stacks.ComposeXStack
    :param settings: The settings for execution
    :type settings: ecs_composex.common.settings.ComposeXSettings
    :param bool is_root: Allows to know whether the stack is parent stack
    """
    for resource_name, resource in root_stack.stack_template.resources.items():
        if isinstance(resource, ComposeXStack) or issubclass(
                type(resource), ComposeXStack):
            LOG.debug(resource)
            LOG.debug(resource.title)
            process_stacks(resource, settings, is_root=False)
            if is_root:
                resource.Parameters.update(
                    {ROOT_STACK_NAME_T: Ref(AWS_STACK_NAME)})
            else:
                resource.Parameters.update(
                    cfn_conditions.pass_root_stack_name())
        elif isinstance(resource, Stack):
            LOG.warning(resource_name)
            LOG.warning(resource)
    root_stack.render(settings)
Exemplo n.º 18
0
    def init_network_settings(
        self, settings: ComposeXSettings, vpc_stack: ComposeXStack
    ) -> None:
        """
        Once we have figured out the compute settings (EXTERNAL vs other)

        :param settings:
        :param vpc_stack:
        :return:
        """
        from ecs_composex.ecs.service_networking.helpers import add_security_group

        self.service_networking = ServiceNetworking(self)
        self.finalize_services_networking_settings(settings)
        if self.service_compute.launch_type == "EXTERNAL":
            LOG.debug(f"{self.name} Ingress cannot be set (EXTERNAL mode). Skipping")
        else:
            if vpc_stack.vpc_resource.mappings:
                self.stack.set_vpc_params_from_vpc_stack_import(vpc_stack)
            else:
                self.stack.set_vpc_parameters_from_vpc_stack(vpc_stack)
            add_security_group(self)
            self.service_networking.ingress.set_aws_sources_ingress(
                settings,
                self.logical_name,
                GetAtt(self.service_networking.security_group, "GroupId"),
            )
            self.service_networking.ingress.set_ext_sources_ingress(
                self.logical_name,
                GetAtt(self.service_networking.security_group, "GroupId"),
            )
            self.service_networking.ingress.associate_aws_ingress_rules(self.template)
            self.service_networking.ingress.associate_ext_ingress_rules(self.template)
            self.service_networking.add_self_ingress()
Exemplo n.º 19
0
def get_tables_list(session, tables=None, next_token=None):
    """
    Function to retrieve the list of all tables

    :param boto3.session.Session session:
    :param list tables: List of tables to add table names to.
    :param str next_token:
    :return:
    """
    if tables is None:
        tables = []
    client = session.client("dynamodb")
    if next_token is None:
        list_r = client.list_tables()
        for table in list_r["TableNames"]:
            tables.append(table)
        if keyisset("LastEvaluatedTableName", list_r):
            return get_tables_list(session, tables,
                                   list_r["LastEvaluatedTableName"])
    elif next_token is not None:
        list_r = client.list_tables(ExclusiveStartTableName=next_token)
        for table in list_r["Tables"]:
            tables.append(table)
    LOG.debug(tables)
    return tables
Exemplo n.º 20
0
def handle_path_settings(props, path_raw):
    """
    Function to set the path and codes properties

    :param dict props:
    :param str path_raw:
    :return:
    """
    path_re = re.compile(
        r"(/[\S][^:]+.$)|(/[\S]+)(?::)((?:[\d]{1,4},?){1,}.$)|((?:[\d]{1,4},?){1,}.$)"
    )
    groups = path_re.search(path_raw).groups()
    if not groups:
        LOG.debug("No PATH or ReturnCodes set.")
        return
    path = groups[0] or groups[1]
    codes = groups[2] or groups[3]
    if path:
        props["HealthCheckPath"] = path
    if codes:
        props["Matcher"] = Matcher(HttpCode=codes)
    if props["HealthCheckProtocol"] not in ["HTTP", "HTTPS"] and codes:
        raise ValueError(
            groups,
            "Protocol and return codes are only valid for HTTP and HTTPS HealthCheck",
        )
Exemplo n.º 21
0
def lookup_cluster_resource(resource, session):
    """
    Function to find the DB in AWS account

    :param boto3.session.Session session: Boto3 session for clients
    :return:
    """
    elasticache_types = {
        "elasticache:cluster": {
            "regexp": r"(?:^arn:aws(?:-[a-z]+)?:elasticache:[\w-]+:[0-9]{12}:cluster:)([\S]+)$"
        }
    }
    res_type = "elasticache:cluster"
    lookup_session = define_lookup_role_from_info(resource.lookup, session)
    cluster_arn = find_aws_resource_arn_from_tags_api(
        resource.lookup,
        lookup_session,
        res_type,
        types=elasticache_types,
        allow_multi=True,
    )
    if not cluster_arn:
        return None
    cluster_config = return_cluster_config(resource, cluster_arn, lookup_session)
    LOG.debug(cluster_config)
    return cluster_config
Exemplo n.º 22
0
def apply_x_configs_to_ecs(settings, root_template, services_stack,
                           services_families, **kwargs):
    """
    Function that evaluates only the x- resources of the root template and iterates over the resources.
    If there is an implemented module in ECS ComposeX for that resource to map to the ECS Services, it will
    execute the function available in the module to apply defined settings to the services stack.

    :param ecs_composex.common.settings.ComposeXSettings settings: The compose file content
    :param troposphere.Template root_template: The root template for ECS ComposeX
    :param ecs_composex.ecs.ServicesStack services_stack: root stack for services.
    :param dict kwargs: settings for building X related resources
    :param dict services_families: Families and services mappings
    """
    for resource_name in root_template.resources:
        resource = root_template.resources[resource_name]
        if (issubclass(type(resource), ComposeXStack)
                and resource_name in SUPPORTED_X_MODULES):
            module = getattr(resource, "title")
            composex_key = f"{X_KEY}{module}"
            ecs_function = get_mod_function(f"{module}.{module}_ecs",
                                            f"{module}_to_ecs")
            if ecs_function:
                LOG.debug(ecs_function)
                ecs_function(
                    settings.compose_content[composex_key],
                    services_stack,
                    services_families,
                    resource,
                    settings,
                )
Exemplo n.º 23
0
    def set_services_scaling_list(self, settings):
        """
        Method to map services and families targets of the services defined.

        :param ecs_composex.common.settings.ComposeXSettings settings:
        :return:
        """
        if not self.services or isinstance(self.services, dict):
            return
        for service in self.services:
            name_key = get_setting_key("name", service)
            scaling_key = get_setting_key("scaling", service)
            if not keyisset(scaling_key, service):
                LOG.debug(
                    f"{self.module.res_key}.{self.name} - No Scaling set for {service[name_key]}"
                )
                continue
            service_name = service[name_key]
            if service_name in settings.families and service_name not in [
                    f[0].name for f in self.families_scaling
            ]:
                self.families_scaling.append(
                    (settings.families[service_name], service[scaling_key]))
            elif service_name in settings.families and service_name in [
                    f[0].name for f in self.families_scaling
            ]:
                LOG.debug(
                    f"{self.module.res_key}.{self.name} - Family {service_name} has already been added. Skipping"
                )
            elif service_name in [s.name for s in settings.services]:
                self.handle_family_scaling_expansion(service, settings)
Exemplo n.º 24
0
def get_mod_function(module_name, function_name):
    """
    Function to get function in a given module name from function_name

    :param module_name: the name of the module in ecs_composex to find and try to import
    :type module_name: str
    :param function_name: name of the function to try to get
    :type function_name: str

    :return: function, if found, from the module
    :rtype: function
    """
    composex_module_name = f"ecs_composex.{module_name}"
    LOG.debug(composex_module_name)
    function = None
    try:
        res_module = import_module(composex_module_name)
        LOG.debug(res_module)
        try:
            function = getattr(res_module, function_name)
            return function
        except AttributeError:
            LOG.info(f"No {function_name} function found - skipping")
    except ImportError as error:
        LOG.error(f"Failure to process the module {composex_module_name}")
        LOG.error(error)
    return function
Exemplo n.º 25
0
    def set_services_targets_from_list(self, settings):
        """
        Deals with services set as a list

        :param settings:
        :return:
        """
        for service in self.services:
            name_key = get_setting_key("name", service)
            access_key = get_setting_key("access", service)
            service_name = service[name_key]
            if service_name in settings.families and service_name not in [
                    f[0].name for f in self.families_targets
            ]:
                self.families_targets.append((
                    settings.families[service_name],
                    True,
                    settings.families[service_name].services,
                    service[access_key],
                    service,
                ))
            elif service_name in settings.families and service_name in [
                    f[0].name for f in self.families_targets
            ]:
                LOG.debug(
                    f"{self.module.res_key}.{self.name} - Family {service_name} has already been added. Skipping"
                )
            elif service_name in [s.name for s in settings.services]:
                self.handle_families_targets_expansion(service, settings)
Exemplo n.º 26
0
def expand_launch_template_tags_specs(lt, tags):
    """
    Function to expand the LaunchTemplate TagSpecifications with defined x-tags.

    :param lt: the LaunchTemplate object
    :type: troposphere.ec2.LaunchTemplate
    :param tags: the Tags as built from x-tags
    :type tags: troposphere.Tags
    """
    LOG.debug("Setting tags to LaunchTemplate")
    try:
        launch_data = getattr(lt, "LaunchTemplateData")
        if hasattr(launch_data, "TagSpecifications"):
            tags_specs = getattr(launch_data, "TagSpecifications")
            if isinstance(tags_specs, list) and tags_specs:
                for tag_spec in tags_specs:
                    if not isinstance(tag_spec, TagSpecifications):
                        continue
                    original_tags = getattr(tag_spec, "Tags")
                    new_tags = original_tags + tags
                    setattr(tag_spec, "Tags", new_tags)
                setattr(launch_data, "TagSpecifications", tags_specs)
    except AttributeError:
        LOG.error("Failed to get the launch template data")
    except Exception as error:
        LOG.error(error)
Exemplo n.º 27
0
    def set_content(self, kwargs, content=None):
        """
        Method to initialize the compose content

        :param dict kwargs:
        :param dict content:
        :return:
        """
        if content is None and len(kwargs[self.input_file_arg]) == 1:
            self.compose_content = load_composex_file(kwargs[self.input_file_arg][0])
        elif content is None and len(kwargs[self.input_file_arg]) > 1:
            files_list = kwargs[self.input_file_arg]
            self.compose_content = load_composex_file(files_list[0])
            files_list.pop(0)
            for file in files_list:
                merge_config_file(self.compose_content, load_composex_file(file))
                LOG.debug(yaml.dump(self.compose_content))

        elif content and isinstance(content, dict):
            self.compose_content = content
        if keyisset("services", self.compose_content):
            render_services_ports(self.compose_content["services"])
        LOG.debug(yaml.dump(self.compose_content))
        interpolate_env_vars(self.compose_content)
        parse_secrets(self)
Exemplo n.º 28
0
def add_secret_to_containers(service_template,
                             db_name,
                             db_def,
                             secret_import,
                             service_name,
                             family_wide=False):
    """
    Function to add DB secret to container

    :param troposphere.Template service_template: the ecs_service template
    :param str db_name: the name of the database used as environment variable name
    :param dict db_def: Definition of the DB.
    :param str secret_import: secret arn
    :param str service_name: Name of the service that was explicitely listed as consuming the DB
    :param bool family_wide: Whether or not apply the secret to all services of the family.
    """

    containers = define_service_containers(service_template)
    db_secrets = [
        EcsSecret(Name=name, ValueFrom=secret_import)
        for name in db_secrets_names(db_name, db_def)
    ]
    for container in containers:
        if (isinstance(container, ContainerDefinition)
                and not isinstance(container.Name,
                                   (Ref, Sub, GetAtt, ImportValue))
                and container.Name.startswith("AWS") and family_wide):
            LOG.debug(f"Ignoring AWS Container {container.Name}")
        elif family_wide:
            for db_secret in db_secrets:
                extend_container_secrets(container, db_secret)
        elif not family_wide and container.Name == service_name:
            for db_secret in db_secrets:
                extend_container_secrets(container, db_secret)
            break
Exemplo n.º 29
0
def generate_resource_permissions(resource_name, policies, attribute, arn=None):
    """
    Function to generate IAM permissions for a given x-resource. Returns the mapping of these for the given resource.

    :param str resource_name: The name of the resource
    :param str attribute: the attribute of the resource we are using for Import
    :param dict policies: the policies associated with the x-resource type.
    :param str arn: The ARN of the resource if already looked up.
    :return: dict of the IAM policies associated with the resource.
    :rtype dict:
    """
    resource_policies = {}
    for a_type in policies:
        clean_policy = {"Version": "2012-10-17", "Statement": []}
        LOG.debug(a_type)
        policy_doc = policies[a_type].copy()
        policy_doc["Sid"] = Sub(f"{a_type}To{resource_name}")
        policy_doc["Resource"] = (
            generate_export_strings(resource_name, attribute) if not arn else arn
        )
        clean_policy["Statement"].append(policy_doc)
        resource_policies[a_type] = IamPolicy(
            PolicyName=Sub(f"{a_type}{resource_name}${{{ROOT_STACK_NAME_T}}}"),
            PolicyDocument=clean_policy,
        )
    return resource_policies
Exemplo n.º 30
0
def invoke_x_to_ecs(
    module_name: str,
    services_stack: ComposeXStack,
    resource: XResource,
    settings: ComposeXSettings,
) -> None:
    """
    Function to associate X resources to Services

    :param None,str module_name: The name of the module managing the resource type
    :param ecs_composex.common.settings.ComposeXSettings settings: The compose file content
    :param ecs_composex.ecs.ServicesStack services_stack: root stack for services.
    :param ecs_composex.common.stacks.ComposeXStack resource: The XStack resource of the module
    :return:
    """
    if module_name is None:
        module_name = resource.name
    composex_key = f"{X_KEY}{module_name}"
    ecs_function = get_mod_function(f"{module_name}.{module_name}_ecs",
                                    f"{module_name}_to_ecs")
    if ecs_function:
        LOG.debug(ecs_function)
        ecs_function(
            settings.compose_content[composex_key],
            services_stack,
            resource,
            settings,
        )