Example #1
0
def define_service_target_group_definition(
    resource,
    family,
    service,
    target_def,
    resources_root_stack,
):
    """
    Function to create the new service TGT Group

    :param ecs_composex.elbv2.elbv2_stack.Elbv2 resource:
    :param service:
    :param ecs_composex.ecs.ecs_family.ComposeFamily family:
    :param dict target_def:
    :param ecs_composex.common.stacks.ComposeXStack resources_root_stack:
    :return:
    """
    if resource.logical_name not in family.stack.DependsOn:
        family.stack.DependsOn.append(resources_root_stack.title)
        LOG.info(
            f"{resource.module.res_key}.{resource.name} - Adding {family.logical_name} {service.name}"
        )

    service_tgt_group = define_service_target_group(
        resource,
        family,
        service,
        resources_root_stack,
        target_def,
    )
    return Ref(service_tgt_group)
Example #2
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,
                    )
def create_new_stream(stream: DeliveryStream) -> None:
    """
    Imports the settings from CFN Definitions and define the CFN Resource from properties

    :param DeliveryStream stream:
    """
    props = import_record_properties(
        stream.properties,
        CfnDeliveryStream,
        ignore_missing_required=True,
        ignore_missing_sub_required=True,
    )
    stream.cfn_resource = CfnDeliveryStream(stream.logical_name, **props)
    stream.log_group = LogGroup(
        f"{stream.logical_name}LogGroup",
        LogGroupName=Sub(f"firehose/${{STACK_ID}}/{stream.name}",
                         STACK_ID=STACK_ID_SHORT),
    )
    if (stream.cfn_resource.DeliveryStreamType == "KinesisStreamAsSource"
            and stream.cfn_resource.DeliveryStreamEncryptionConfigurationInput
            != NoValue):
        LOG.error(
            f"{stream.module.res_key}.{stream.name} -"
            " You can only have ServerSide encryption with DirectPut DeliveryStream. Removing."
        )
        stream.cfn_resource.DeliveryStreamEncryptionConfigurationInput = NoValue
    set_replace_iam_role(stream)
    values_validation(stream)
    stream.init_outputs()
    stream.generate_outputs()
Example #4
0
def deploy(settings, root_stack):
    """
    Function to deploy (create or update) the stack to CFN.
    :param ComposeXSettings settings:
    :param ComposeXStack root_stack:
    :return:
    """
    validate_stack_availability(settings, root_stack)
    client = settings.session.client("cloudformation")
    if assert_can_create_stack(client, settings.name):
        res = client.create_stack(
            StackName=settings.name,
            Capabilities=["CAPABILITY_IAM", "CAPABILITY_AUTO_EXPAND"],
            Parameters=root_stack.render_parameters_list_cfn(),
            TemplateURL=root_stack.TemplateURL,
        )
        LOG.info(f"Stack {settings.name} successfully deployed.")
        LOG.info(res["StackId"])
        return res["StackId"]
    elif assert_can_update_stack(client, settings.name):
        LOG.warning(f"Stack {settings.name} already exists. Updating.")
        res = client.update_stack(
            StackName=settings.name,
            Capabilities=["CAPABILITY_IAM", "CAPABILITY_AUTO_EXPAND"],
            Parameters=root_stack.render_parameters_list_cfn(),
            TemplateURL=root_stack.TemplateURL,
        )
        LOG.info(f"Stack {settings.name} successfully updating.")
        LOG.info(res["StackId"])
        return res["StackId"]
    return None
def lookup_rds_secret(rds_resource, secret_lookup):
    """
    Lookup RDS DB Secret specified

    :param ecs_composex.compose.x_resources.network_x_resources.DatabaseXResource rds_resource:
    :param secret_lookup:
    :return:
    """
    if keyisset("Arn", secret_lookup):
        client = rds_resource.lookup_session.client("secretsmanager")
        try:
            secret_arn = client.describe_secret(
                SecretId=secret_lookup["Arn"])["ARN"]

        except client.exceptions.ResourceNotFoundException:
            LOG.error(f"{rds_resource.module.res_key}.{rds_resource.name}"
                      f" - Secret {secret_lookup['Arn']} not found")
            raise
        except ClientError as error:
            LOG.error(error)
            raise
    elif keyisset("Tags", secret_lookup):
        secret_arn = find_aws_resource_arn_from_tags_api(
            rds_resource.lookup["secret"],
            rds_resource.lookup_session,
            "secretsmanager:secret",
        )
    else:
        raise LookupError(f"{rds_resource.module.res_key}.{rds_resource.name}"
                          " - Failed to find the DB Secret")
    if secret_arn:
        rds_resource.lookup_properties[
            rds_resource.db_secret_arn_parameter] = secret_arn
Example #6
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)
Example #7
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)
def lookup_mesh_by_name(session, mesh_name, mesh_owner=None):
    """
    Function to figure out whether the mesh exists or not.

    :param str mesh_name:
    :param boto3.session.Session session:
    :param str mesh_owner:
    :return:
    """
    r_params = {
        "meshName": mesh_name,
    }
    if mesh_owner is not None:
        r_params["meshOwner"] = mesh_owner
    client = session.client("appmesh")
    try:
        mesh_r = client.describe_mesh(**r_params)["mesh"]
        mesh_info = {
            MESH_NAME.title: mesh_r["meshName"],
            MESH_OWNER_ID.title: mesh_r["metadata"]["meshOwner"],
        }
        LOG.info(
            f"Found mesh {mesh_name} owned by {mesh_info[MESH_OWNER_ID.title]}"
        )
        return mesh_info
    except client.exceptions.NotFoundException:
        LOG.info(
            f"No mesh {mesh_name} found owned with current details. Looking for shared meshes."
        )
        mesh_info = find_mesh_in_list(mesh_name, client)
        return mesh_info
Example #9
0
    def set_azs_from_vpc_import(self, subnets, session=None):
        """
        Function to get the list of AZs for a given set of subnets

        :param dict subnets:
        :param session: The Session used to find the EC2 subnets (useful for lookup).
        :return:
        """
        if session is None:
            client = self.lookup_session.client("ec2")
        else:
            client = session.client("ec2")
        for subnet_name, subnet_definition in subnets.items():
            if not isinstance(subnet_definition, list):
                continue
            for subnet_param in self.subnets_parameters:
                if subnet_param.title == subnet_name:
                    subnets_param = subnet_param
                    break
            else:
                raise KeyError(
                    f"x-vpc.set_azs_from_vpc_import - No parameter defined for {subnet_name}"
                )
            try:
                subnets_r = client.describe_subnets(
                    SubnetIds=subnet_definition)["Subnets"]
                azs = [subnet["AvailabilityZone"] for subnet in subnets_r]
                self.mappings[subnet_name]["Azs"] = azs
                self.azs[subnets_param] = azs
            except ClientError:
                LOG.warning(
                    "Could not define the AZs based on the imported subnets")
Example #10
0
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
Example #11
0
def find_mesh_in_list(mesh_name, client, next_token=None):
    """
    Function to recursively go through meshes in case the mesh exists but we don't know the account Id

    :param mesh_name: Name of the mesh
    :param next_token: token for next api call
    :return:
    """
    if next_token is not None:
        mesh_r = client.list_meshes(nexToken=next_token)
    else:
        mesh_r = client.list_meshes()
    if not keyisset("meshes", mesh_r):
        return {}
    for mesh in mesh_r["meshes"]:
        if mesh["meshName"] == mesh_name:
            mesh_info = {
                MESH_NAME.title: mesh["meshName"],
                MESH_OWNER_ID.title: mesh["meshOwner"],
            }
            LOG.info(
                f"Found shared mesh {mesh_name} owned by {mesh_info[MESH_OWNER_ID.title]}"
            )
            return mesh_info
    if keyisset("nextToken", mesh_r):
        return find_mesh_in_list(mesh_name, client, mesh_r["nextToken"])
Example #12
0
    def set_content(self, kwargs, content=None, fully_load=True):
        """
        Method to initialize the compose content

        :param dict kwargs:
        :param dict content:
        :param bool fully_load:
        """
        files = (
            []
            if not keyisset(self.input_file_arg, kwargs)
            else kwargs[self.input_file_arg]
        )
        content_def = ComposeDefinition(files, content)
        self.compose_content = content_def.definition
        source = pkg_files("ecs_composex").joinpath("specs/compose-spec.json")
        LOG.info(f"Validating against input schema {source}")
        resolver = jsonschema.RefResolver(
            f"file://{path.abspath(path.dirname(source))}/", None
        )
        jsonschema.validate(
            content_def.definition,
            loads(source.read_text()),
            resolver=resolver,
        )
        if fully_load:
            self.set_secrets()
            self.set_volumes()
            self.set_services()
            self.set_families()
            self.set_efs()
Example #13
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
Example #14
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,
                )
def get_replica_group_config(resource, cluster_name, session):
    client = session.client("elasticache")
    try:
        cluster_r = client.describe_replication_groups(ReplicationGroupId=cluster_name)
        cluster = cluster_r["ReplicationGroups"][0]
        node_r = client.describe_cache_clusters(
            CacheClusterId=cluster["MemberClusters"][0]
        )
        sg_id = node_r["CacheClusters"][0]["SecurityGroups"][0]["SecurityGroupId"]
        resource.port_attr = elasticache_params.REPLICA_PRIMARY_PORT
        return {
            elasticache_params.REPLICA_PRIMARY_ADDRESS.title: cluster["NodeGroups"][0][
                "PrimaryEndpoint"
            ]["Address"],
            elasticache_params.REPLICA_PRIMARY_PORT.title: cluster["NodeGroups"][0][
                "PrimaryEndpoint"
            ]["Port"],
            elasticache_params.REPLICA_READ_ENDPOINT_ADDRESSES.title: [
                cluster["NodeGroups"][0]["ReaderEndpoint"]["Address"]
            ],
            elasticache_params.REPLICA_READ_ENDPOINT_PORTS.title: [
                cluster["NodeGroups"][0]["ReaderEndpoint"]["Port"]
            ],
            elasticache_params.CLUSTER_SG.title: [sg_id],
        }
    except client.exceptions.ReplicationGroupNotFoundFault as error:
        LOG.error(f"Could not fetch information about {cluster_name}")
        LOG.error(error)
        return None
Example #16
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
Example #17
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)
Example #18
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
Example #19
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 __init__(self, definition, ports):
        """
        Initialize network settings for the family ServiceConfig
        """
        self.definition = deepcopy(definition)

        self.aws_sources = (self.definition[self.aws_sources_key] if keyisset(
            self.aws_sources_key, self.definition) else [])
        self.ext_sources = (self.definition[self.ext_sources_key] if keyisset(
            self.ext_sources_key, self.definition) else [])
        self.ext_sources = []
        if keyisset(self.ext_sources_key, self.definition):
            cidrs = []
            for ext_source in self.definition[self.ext_sources_key]:
                source_cidr = set_else_none(
                    self.ipv4_key,
                    ext_source,
                    set_else_none(self.ipv6_key, ext_source, None),
                )
                if source_cidr and source_cidr not in cidrs:
                    self.ext_sources.append(ext_source)
                else:
                    LOG.warning(
                        f"Ingress source {source_cidr} already defined in a previous Ingress rule."
                    )

        self.services = (self.definition[self.services_key] if keyisset(
            self.services_key, self.definition) else [])
        self.ports = ports
        self.aws_ingress_rules = []
        self.ext_ingress_rules = []
        self.to_self_rules = []
Example #21
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
    )
Example #22
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
Example #23
0
    def ephemeral_storage(self):
        storage_key = "ecs.ephemeral.storage"
        storage_value = set_else_none(storage_key,
                                      set_else_none("labels",
                                                    self.deploy,
                                                    alt_value={}),
                                      alt_value=0)
        if isinstance(storage_value, (int, float)):
            ephemeral_storage = int(storage_value)
        elif isinstance(storage_value, str):
            ephemeral_storage = int(set_memory_to_mb(storage_value) / 1024)
        else:
            raise TypeError(
                f"The value for {storage_key} is of type",
                type(storage_value),
                "Expected one of",
                [int, float, str],
            )
        if ephemeral_storage <= 21:
            return 0

        elif ephemeral_storage > 200:
            return 200
        else:
            LOG.info(f"{self.name} - {storage_key} set to {ephemeral_storage}")
            return int(ephemeral_storage)
Example #24
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
Example #25
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)
Example #26
0
def add_vpc_to_root(root_stack, settings):
    """
    Function to figure whether to create the VPC Stack and if not, set the parameters.

    :param root_stack:
    :param settings:
    :return: vpc_stack
    :rtype: VpcStack
    """
    vpc_stack = None
    vpc_xkey = f"{X_KEY}{RES_KEY}"

    if keyisset(vpc_xkey, settings.compose_content):
        if keyisset("Lookup", settings.compose_content[vpc_xkey]):
            x_settings = lookup_x_vpc_settings(
                settings.session, settings.compose_content[vpc_xkey]["Lookup"])
            apply_vpc_settings(x_settings, root_stack)
        elif keyisset("Use", settings.compose_content[vpc_xkey]):
            x_settings = import_vpc_settings(
                settings.compose_content[vpc_xkey]["Use"])
            apply_vpc_settings(x_settings, root_stack)
        else:
            if keyisset("Create",
                        settings.compose_content[vpc_xkey]) and keyisset(
                            "Lookup", settings.compose_content[vpc_xkey]):
                LOG.warning("We have both Create and Lookup set for x-vpc."
                            "Creating a new VPC")
            vpc_stack = create_new_vpc(vpc_xkey, settings)
    else:
        LOG.info(f"No {vpc_xkey} detected. Creating a new VPC.")
        vpc_stack = create_new_vpc(vpc_xkey, settings, default=True)
    if isinstance(vpc_stack, VpcStack):
        root_stack.stack_template.add_resource(vpc_stack)
    return vpc_stack
def scan_poll_and_wait(registry, repository_name, image, image_url, ecr_session=None):
    """
    Function to pull the scans results until no longer in progress

    :param boto3.session.Session ecr_session:
    :param registry:
    :param repository_name:
    :param image:
    :param image_url:
    :param ecr_session:
    :return: The scan report
    :rtype: dict
    """
    client = ecr_session.client("ecr")
    while True:
        try:
            image_scan_r = client.describe_image_scan_findings(
                registryId=registry,
                repositoryName=repository_name,
                imageId=image,
            )
            if image_scan_r["imageScanStatus"]["status"] == "IN_PROGRESS":
                LOG.info(f"{image_url} - Scan in progress - waiting 10 seconds")
                sleep(10)
            else:
                return image_scan_r
        except client.exceptions.LimitExceededException:
            LOG.warn(f"{image_url} - Exceeding API Calls quota. Waiting 10 seconds")
            sleep(10)
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
Example #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
Example #30
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",
        )