def create_root_template(new_resources: list, module_res_key: str) -> Template:
    """
    Function to create the root stack template for profiles

    :param list[CodeProfiler] new_resources:
    :param str module_res_key:
    :return: The template wit the profiles
    :rtype: troposphere.Template
    """
    root_tpl = build_template(f"Root stack to manage {module_res_key}")
    for res in new_resources:
        try:
            props = import_record_properties(res.properties,
                                             ProfilingGroup,
                                             ignore_missing_required=False)
            if res.parameters and keyisset("AppendStackId", res.parameters):
                props["ProfilingGroupName"] = Sub(
                    f"{res.properties['ProfilingGroupName']}-${{StackId}}",
                    StackId=STACK_ID_SHORT,
                )
        except KeyError:
            props = import_record_properties(res.properties,
                                             ProfilingGroup,
                                             ignore_missing_required=True)
            props["ProfilingGroupName"] = Sub(
                f"{res.logical_name}-${{StackId}}", StackId=STACK_ID_SHORT)
        res.cfn_resource = ProfilingGroup(res.logical_name, **props)
        res.init_outputs()
        res.generate_outputs()
        add_outputs(root_tpl, res.outputs)
        root_tpl.add_resource(res.cfn_resource)
    return root_tpl
Beispiel #2
0
def create_alarms(template: Template, new_alarms: list[Alarm]) -> None:
    """
    Main function to create new alarms
    Rules out CompositeAlarms first, creates "Simple" alarms, and then link these to ComopsiteAlarms if so declared.
    """
    for alarm in new_alarms:
        if (alarm.properties and not alarm.parameters or
            (alarm.parameters
             and not keyisset("CompositeExpression", alarm.parameters))):
            try:
                import_record_properties(
                    alarm.properties,
                    CompositeAlarm,
                    ignore_missing_required=False,
                )
            except KeyError:
                props = import_record_properties(alarm.properties, CWAlarm)
                alarm.cfn_resource = CWAlarm(alarm.logical_name, **props)
                if alarm.cfn_resource.title not in template.resources:
                    alarm.init_outputs()
                    alarm.generate_outputs()
                    add_resource(template, alarm.cfn_resource)
                    add_outputs(template, alarm.outputs)
        elif alarm.parameters and keyisset("CompositeExpression",
                                           alarm.parameters):
            continue

    add_composite_alarms(template, new_alarms)
Beispiel #3
0
def set_parameters_groups_from_macro_parameters(db, template):
    """
    Function to set the DB parameters group if ParametersGroups is set on MacroParameters
    """
    if isinstance(db.cfn_resource, DBCluster):
        props = import_record_properties(
            db.parameters["ParametersGroups"], DBClusterParameterGroup
        )
        params = template.add_resource(
            DBClusterParameterGroup(
                CLUSTER_PARAMETER_GROUP_T,
                **props,
            )
        )
        setattr(db.cfn_resource, "DBClusterParameterGroupName", Ref(params))
    elif isinstance(db.cfn_resource, DBInstance):
        props = import_record_properties(
            db.parameters["ParametersGroups"], DBParameterGroup
        )
        params = template.add_resource(
            DBParameterGroup(
                CLUSTER_PARAMETER_GROUP_T,
                **props,
            )
        )
        setattr(db.cfn_resource, "DBParameterGroupName", Ref(params))
def define_new_namespace(new_namespaces, stack_template):
    """
    Creates new AWS CloudMap namespaces and associates it with the stack template

    :param list[PrivateNamespace] new_namespaces: list of PrivateNamespace to process
    :param troposphere.Template stack_template: The template to add the new resources to
    """
    for namespace in new_namespaces:
        if namespace.properties:
            if (
                keyisset("Name", namespace.properties)
                and namespace.zone_name != namespace.properties["Name"]
            ):
                raise ValueError(
                    f"{namespace.module.res_key}.{namespace.name} - "
                    "ZoneName and Properties.Name must be the same value when set."
                )
            elif not keyisset("Name", namespace.properties):
                namespace.properties["Name"] = namespace.zone_name

            namespace_props = import_record_properties(
                namespace.properties, PrivateNamespace
            )
            if keyisset("Vpc", namespace_props):
                LOG.warn(
                    f"{namespace.module.res_key}.{namespace.name} - "
                    "Vpc property was set. Overriding to compose-x x-vpc defined for execution."
                )
            namespace_props["Vpc"] = f"x-vpc::{VPC_ID.title}"
            namespace.cfn_resource = PrivateNamespace(
                namespace.logical_name, **namespace_props
            )
        elif namespace.uses_default:
            namespace_props = import_record_properties(
                {"Name": namespace.zone_name, "Vpc": f"x-vpc::{VPC_ID.title}"},
                PrivateDnsNamespace,
            )
            namespace.cfn_resource = PrivateDnsNamespace(
                namespace.logical_name, **namespace_props
            )
        if not namespace.cfn_resource:
            raise AttributeError(
                f"{namespace.module.res_key}.{namespace.name} - "
                "Failed to create PrivateNamespace from Properties/MacroParameters"
            )
        stack_template.add_resource(namespace.cfn_resource)
        namespace.init_outputs()
        namespace.generate_outputs()
Beispiel #5
0
def create_efs_stack(settings, new_resources):
    """
    Function to create the root stack and add EFS FS.

    :param ecs_composex.common.settings.ComposeXSettings settings:
    :param list new_resources:
    :return: Root template for EFS
    :rtype: troposphere.Template
    """
    template = build_template("Root for EFS built by ECS Compose-X", [FS_PORT])
    for res in new_resources:
        res_cfn_props = import_record_properties(res.properties, FileSystem)
        res.cfn_resource = FileSystem(res.logical_name, **res_cfn_props)
        res.db_sg = SecurityGroup(
            f"{res.logical_name}SecurityGroup",
            GroupName=Sub(f"{res.logical_name}EfsSg"),
            GroupDescription=Sub(f"SG for EFS {res.cfn_resource.title}"),
            VpcId=Ref(VPC_ID),
        )
        template.add_resource(res.cfn_resource)
        template.add_resource(res.db_sg)
        res.init_outputs()
        res.generate_outputs()
        template.add_output(res.outputs)
    return template
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()
Beispiel #7
0
def render_new_parameters(new_resources: list[SsmParameter],
                          root_stack: ComposeXStack) -> None:
    """

    :param list[SsmParameter] new_resources:
    :param ecs_composex.common.stacks.ComposeXStack root_stack:
    """
    for new_res in new_resources:
        value = None
        if (keyisset("Type", new_res.definition)
                and new_res.definition["Type"] == "SecureString"):
            raise ValueError(
                f"{new_res.name} AWS CFN does not support SecureString.")
        if new_res.parameters and keyisset("FromFile", new_res.parameters):
            value = import_value_from_file(new_res)
        if keyisset("Value", new_res.properties):
            if value:
                LOG.warn(
                    "Both Value and FromFile properties were set. Using Value from Properties"
                )
            value = new_res.properties["Value"]
        if not value:
            raise ValueError(f"{new_res.name} - Failed to determine the value")
        if keyisset("EncodeToBase64", new_res.parameters):
            value = Base64(value)
        new_res.properties.update({"Value": value})
        param_props = import_record_properties(new_res.properties,
                                               CfnSsmParameter,
                                               ignore_missing_required=False)
        new_res.cfn_resource = CfnSsmParameter(new_res.logical_name,
                                               **param_props)
        root_stack.stack_template.add_resource(new_res.cfn_resource)
        new_res.init_outputs()
        new_res.generate_outputs()
        add_outputs(root_stack.stack_template, new_res.outputs)
Beispiel #8
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)
def define_table(table, template):
    """
    Function to create the DynamoDB table resource

    :param table:
    :type table: ecs_composex.common.compose_resources.Table
    """
    table_props = import_record_properties(table.properties, dynamodb.Table)
    table_props.update({
        "Metadata":
        metadata,
        "Tags":
        Tags(
            Name=table.name,
            ResourceName=table.logical_name,
            CreatedByComposex=True,
            RootStackName=Ref(ROOT_STACK_NAME),
        ),
    })
    cfn_table = dynamodb.Table(table.logical_name, **table_props)
    table.cfn_resource = cfn_table
    if table.scaling:
        add_autoscaling(table, template)
    table.init_outputs()
    table.generate_outputs()
    add_resource(template, table.cfn_resource)
    add_outputs(template, table.outputs)
def set_db_cluster(template, db, secret, sgs):
    """
    Function to parse and transform yaml definition to Troposphere

    :param troposphere.Template template:
    :param ecs_composex.docdb.docdb_stack.DocDb db:
    :param troposphere.secretsmanager.Secret secret:
    :param list sgs:
    """
    props = import_record_properties(db.properties, docdb.DBCluster)
    if not keypresent("StorageEncrypted", props):
        props["StorageEncrypted"] = True
    props.update({
        "VpcSecurityGroupIds":
        sgs,
        "MasterUsername":
        Sub(f"{{{{resolve:secretsmanager:${{{secret.title}}}:SecretString:username}}}}"
            ),
        "MasterUserPassword":
        Sub(f"{{{{resolve:secretsmanager:${{{secret.title}}}:SecretString:password}}}}"
            ),
        "DBSubnetGroupName":
        Ref(db.db_subnets_group),
    })
    if db.parameters and keyisset("DBClusterParameterGroup", db.parameters):
        parameter_group = template.add_resource(add_parameters_group(db))
        props["DBClusterParameterGroupName"] = Ref(parameter_group)
    db.cfn_resource = docdb.DBCluster(db.logical_name, **props)
    template.add_resource(db.cfn_resource)
def create_replication_group(cluster):
    """
    Function to add the replication group from properties

    :param ecs_composex.elasticache.elasticache_stack.CacheCluster cluster:
    :return:
    """
    if (
        not cluster.cfn_resource
        and isinstance(cluster.cfn_resource, CacheCluster)
        and cluster.cfn_resource.Engine == "redis"
    ):
        raise ValueError("Replication group can only be set for a Redis cache cluster.")

    to_del = ["CacheSecurityGroupNames", "PreferredCacheClusterAZs"]
    props = import_record_properties(
        cluster.parameters["ReplicationGroup"], ReplicationGroup
    )
    for prop_to_del in to_del:
        if keyisset(prop_to_del, props):
            del props[prop_to_del]
    if not (
        keyisset("NumCacheClusters", props)
        or keyisset("NumNodeGroups", props)
        or keyisset("ReplicasPerNodeGroup", props)
    ):
        props["PrimaryClusterId"] = Ref(cluster.cfn_resource)
    props["SecurityGroupIds"] = [GetAtt(cluster.db_sg, "GroupId")]
    if cluster.parameter_group:
        props["CacheParameterGroupName"] = Ref(cluster.parameter_group)
    group = ReplicationGroup(f"{cluster.logical_name}ReplicationGroup", **props)
    return group
Beispiel #12
0
def add_parameters_group(db):
    """
    Function to create the DBClusterParameterGroup to associate with the cluster

    :param ecs_composex.docdb_stack.DocDb db:
    :return: parameter group
    :rtype: DBClusterParameterGroup
    """
    props = import_record_properties(db.parameters["DBClusterParameterGroup"],
                                     DBClusterParameterGroup)
    return DBClusterParameterGroup(f"{db.logical_name}ParametersGroup",
                                   **props)
Beispiel #13
0
    def handle_cognito_pools(self, settings, listener_stack):
        """

        :param ecs_composex.common.settings.ComposeXSettings settings:
        :param ecs_composex.common.stacks.ComposeXStack listener_stack:
        :return:
        """
        cognito_auth_key = "AuthenticateCognitoConfig"
        for target in self.services:
            if keyisset("CreateCognitoClient", target):
                user_pool_client_params = target["CreateCognitoClient"]
                pool_id = user_pool_client_params["UserPoolId"]
                pool_params = import_cognito_pool(pool_id, settings,
                                                  listener_stack)
                user_pool_client_params["UserPoolId"] = pool_params[0]
                user_pool_client_props = import_record_properties(
                    user_pool_client_params, UserPoolClient)
                user_pool_client = listener_stack.stack_template.add_resource(
                    UserPoolClient(
                        f"{listener_stack.title}UserPoolClient{NONALPHANUM.sub('', target['name'])}",
                        **user_pool_client_props,
                    ))
                if keyisset(cognito_auth_key, target):
                    target[cognito_auth_key]["UserPoolArn"] = pool_params[1]
                    target[cognito_auth_key]["UserPoolDomain"] = pool_params[2]
                    target[cognito_auth_key]["UserPoolClientId"] = Ref(
                        user_pool_client)
                else:
                    LOG.warning(
                        "No AuthenticateCognitoConfig defined. Setting to default settings"
                    )
                    target.update({
                        cognito_auth_key: {
                            "OnUnauthenticatedRequest": "authenticate",
                            "Scope": "openid email profile",
                            "UserPoolArn": pool_params[1],
                            "UserPoolDomain": pool_params[2],
                            "UserPoolClientId": Ref(user_pool_client),
                        }
                    })
                del target["CreateCognitoClient"]
            elif (not keyisset("CreateCognitoClient", target)
                  and keyisset(cognito_auth_key, target)
                  and keyisset("UserPoolArn", target[cognito_auth_key])
                  and target[cognito_auth_key]["UserPoolArn"].startswith(
                      "x-cognito")):
                pool_id = target[cognito_auth_key]["UserPoolArn"].split(
                    r"::")[-1]
                pool_params = import_cognito_pool(pool_id, settings,
                                                  listener_stack)
                target[cognito_auth_key]["UserPoolArn"] = pool_params[1]
                target[cognito_auth_key]["UserPoolDomain"] = pool_params[2]
def create_parameter_group(cluster, definition):
    """
    Function to add the parameter group

    :param ecs_composex.elasticache.elasticache_stack.CacheCluster cluster:
    :param dict definition:
    :return: The parameter group
    :rtype: troposphere.elasticache.ParameterGroup
    """
    props = import_record_properties(definition, ParameterGroup)
    cluster.parameter_group = ParameterGroup(
        f"{cluster.logical_name}ParameterGroup", **props
    )
Beispiel #15
0
def define_event_rule(stack: ComposeXStack, rule: Rule) -> None:
    """
    Function to define the EventRule properties

    :param ecs_composex.common.stacks.ComposeXStack stack:
    :param ecs_composex.events.events_stack.Rule rule:
    """
    rule_props = import_record_properties(rule.properties, CfnRule)
    if not keyisset("Targets", rule_props):
        rule_props["Targets"] = []
    if not keyisset("Name", rule_props) or rule_props["Name"] == "":
        rule_props["Name"] = NoValue
    rule.cfn_resource = CfnRule(rule.logical_name, **rule_props)
    stack.stack_template.add_resource(rule.cfn_resource)
Beispiel #16
0
def add_instances_from_parameters(db_template, db):
    """
    Function to go over each Instance defined in parameters

    :param troposphere.Template db_template: The template to add the resources to.
    :param ecs_composex.rds.rds_stack.Rds db: The Db object defined in compose.
    :raises: TypeError
    """
    aurora_compatible = [
        "Engine",
        "UseDefaultProcessorFeatures",
        "Tags",
        "SourceRegion",
        "SourceDBInstanceIdentifier",
        "PubliclyAccessible",
        "PromotionTier",
        "ProcessorFeatures",
        "PreferredMaintenanceWindow",
        "EnablePerformanceInsights",
        "AllowMajorVersionUpgrade",
        "AssociatedRoles",
        "CACertificateIdentifier",
        "DBInstanceClass",
        "DBParameterGroupName",
    ]
    if not isinstance(db.parameters["Instances"], list):
        raise TypeError("The Instances in MacroParameters must be a list of dict")
    for count, db_instance in enumerate(db.parameters["Instances"]):
        if not isinstance(db_instance, dict):
            raise TypeError(
                "The instance defined must be the CFN properties for RDS Instance. Got",
                type(db_instance),
            )
        instance_props = import_record_properties(db_instance, DBInstance)
        instance_props["Engine"] = Ref(DB_ENGINE_NAME)

        to_del = [
            prop_name
            for prop_name in instance_props.keys()
            if prop_name not in aurora_compatible
        ]
        for key in to_del:
            del instance_props[key]
        db_instance = DBInstance(
            f"{db.logical_name}Instance{count}",
            DBClusterIdentifier=Ref(db.cfn_resource),
            **instance_props,
        )
        db_template.add_resource(db_instance)
def create_new_stream(stream):
    """
    Function to create the new Kinesis stream
    :param ecs_composex.kinesis.kinesis_stack.Stream stream:
    :param ecs_composex.common.settings.ComposeXSettings settings:
    :return:
    """
    props = import_record_properties(stream.properties, Stream)
    if not keyisset("ShardCount", stream.properties):
        LOG.warning("ShardCount must be set. Defaulting to 1")
        props["ShardCount"] = 1
    props["Tags"] = Tags(Name=stream.logical_name, ComposeName=stream.name)
    stream.cfn_resource = Stream(stream.logical_name, **props)
    stream.init_outputs()
    stream.generate_outputs()
def generate_bucket(bucket: Bucket) -> None:
    """
    Function to generate the S3 bucket object

    :param ecs_composex.s3.s3_bucket.Bucket bucket:
    """
    bucket_name = define_bucket_name(bucket)
    final_bucket_name = (Sub(bucket_name) if isinstance(bucket_name, str) and
                         (bucket_name.find(AWS_REGION) >= 0
                          or bucket_name.find(AWS_ACCOUNT_ID) >= 0) else
                         bucket_name)
    LOG.debug(bucket_name)
    LOG.debug(final_bucket_name)
    props = import_record_properties(bucket.properties, s3.Bucket)
    props["BucketName"] = final_bucket_name
    bucket.cfn_resource = s3.Bucket(bucket.logical_name, **props)
Beispiel #19
0
    def create_acm_cert(self):
        """
        Method to set the ACM Certificate definition
        """
        if self.properties:
            props = import_record_properties(self.properties, CfnAcmCertificate)
        elif self.parameters:
            props = self.define_parameters_props()
        else:
            raise ValueError(
                "Failed to determine how to create the ACM certificate",
                self.logical_name,
            )

        self.cfn_resource = CfnAcmCertificate(f"{self.logical_name}AcmCert", **props)
        self.init_outputs()
        self.generate_outputs()
Beispiel #20
0
def create_new_domains(new_domains, stack):
    """
    Function to create the new CFN Template for the OS Domains to create

    :param list[ecs_composex.opensearch.opensearch_stack.OpenSearchDomain] new_domains:
    :param ecs_composex.common.stacks.ComposeXStack stack:
    """
    for domain in new_domains:
        domain.set_override_subnets()
        props = import_record_properties(domain.properties,
                                         opensearchservice.Domain)
        if keyisset("VPCOptions", props) or domain.subnets_override:
            add_new_security_group(domain, props, stack)
        if domain.parameters:
            apply_domain_parameters(domain, stack, props)
        if keyisset("AdvancedSecurityOptions", props):
            correcting_required_settings(domain, props)
        validate_instance_types(domain, props)
        domain.cfn_resource = opensearchservice.Domain(domain.logical_name,
                                                       **props)
        domain.init_outputs()
        stack.stack_template.add_resource(domain.cfn_resource)
        domain.generate_outputs()
        if domain.security_group:
            domain.add_new_output_attribute(
                OS_DOMAIN_SG,
                (
                    f"{domain.logical_name}{OS_DOMAIN_SG.return_value}",
                    domain.security_group,
                    GetAtt,
                    OS_DOMAIN_SG.return_value,
                ),
            )
            domain.add_new_output_attribute(
                OS_DOMAIN_PORT,
                (
                    f"{domain.logical_name}{OS_DOMAIN_PORT.title}",
                    OS_DOMAIN_PORT.Default,
                    OS_DOMAIN_PORT.Default,
                    False,
                ),
            )
            add_parameters(stack.stack_template, [OS_DOMAIN_PORT])
        add_outputs(stack.stack_template, domain.outputs)
Beispiel #21
0
 def define_cluster(self, root_stack, settings: ComposeXSettings):
     """
     Function to create the cluster from provided properties.
     """
     props = import_record_properties(self.properties, Cluster)
     props["Metadata"] = metadata
     if not keyisset("ClusterName", props):
         props["ClusterName"] = Ref(AWS_STACK_NAME)
     if keyisset("DefaultCapacityProviderStrategy",
                 props) and not keyisset("CapacityProviders", props):
         raise KeyError("When specifying DefaultCapacityProviderStrategy"
                        " you must specify CapacityProviders")
     cluster_name = props["ClusterName"]
     if self.parameters:
         configuration = self.update_props_from_parameters(
             cluster_name, root_stack, settings)
         props["Configuration"] = configuration
     self.cfn_resource = Cluster(CLUSTER_T, **props)
     root_stack.stack_template.add_resource(self.cfn_resource)
     self.cluster_identifier = Ref(self.cfn_resource)
Beispiel #22
0
def set_db_cluster(template, db, sgs):
    """
    Function to parse and transform yaml definition to Troposphere

    :param troposphere.Template template:
    :param ecs_composex.docdb_stack.DocDb db:
    :param troposphere.secretsmanager.Secret secret:
    :param list sgs:
    """
    props = import_record_properties(db.properties, DBCluster)
    if not keypresent("StorageEncrypted", props):
        props["StorageEncrypted"] = True
    props.update({
        "VpcSecurityGroupIds": sgs,
    })
    if db.parameters and keyisset("DBClusterParameterGroup", db.parameters):
        parameter_group = template.add_resource(add_parameters_group(db))
        props["DBClusterParameterGroupName"] = Ref(parameter_group)
    db.cfn_resource = DBCluster(db.logical_name, **props)
    template.add_resource(db.cfn_resource)
Beispiel #23
0
def create_composite_alarm(alarm: Alarm, alarms: list[Alarm]) -> None:
    """
    Function to create the composite alarms
    """
    if alarm.properties and keyisset("AlarmRule", alarm.properties):
        eval_expression = alarm.properties["AlarmRule"]
    elif alarm.parameters and keyisset("CompositeExpression",
                                       alarm.parameters):
        eval_expression = alarm.parameters["CompositeExpression"]
    else:
        raise KeyError(
            "Either Properties.AlarmRule or MacroParameters.CompositeExpression must be set",
            alarm.properties,
            alarm.parameters,
        )
    mapping = map_expression_to_alarms(eval_expression, alarms)
    composite_expression = create_composite_alarm_expression(
        mapping, eval_expression)
    stack_id = Select(4, Split("-", Select(2, Split("/", Ref(AWS_STACK_ID)))))
    alarm_name = f"${{{AWS_REGION}}}-${{StackId}}-CompositeAlarmFor" + "".join(
        [a.title for a in mapping.values()])
    alarm_name = (alarm_name[:(254 - 12)] if len(alarm_name) >
                  (254 - 12) else alarm_name)
    if alarm.properties:
        props = import_record_properties(alarm.properties, CompositeAlarm)
        props.update({
            "AlarmRule": composite_expression,
            "AlarmName": Sub(alarm_name, StackId=stack_id),
        })
    else:
        props = {
            "AlarmRule": composite_expression,
            "AlarmName": Sub(alarm_name, StackId=stack_id),
            "ActionsEnabled": True,
        }
    alarm.properties = props
    alarm.cfn_resource = CompositeAlarm(
        alarm.logical_name,
        DependsOn=[a.title for a in mapping.values()],
        **props,
    )
Beispiel #24
0
def define_target_conditions(definition) -> list:
    """
    Function to create the conditions for forward to target

    :param definition:
    :return: list of conditions
    :rtype: list
    """
    conditions = []
    if keyisset("Conditions", definition) and isinstance(
            definition["Conditions"], list):
        conditions = import_record_properties(
            {"Conditions": definition["Conditions"]},
            ListenerRule,
            set_to_novalue=False,
            ignore_missing_required=True,
        )["Conditions"]
    elif keyisset("access", definition) and isinstance(definition["access"],
                                                       str):
        return handle_string_condition_format(definition["access"])
    return conditions
Beispiel #25
0
def add_db_instances(template, db):
    """
    Function to add DB Instances either based on properties or default.
    Default is to add one DB Instance, the smallest size there is.

    :param troposphere.Template template:
    :param ecs_composex.docdb_stack.DocDb db:
    :return:
    """
    if not db.parameters or not keyisset("Instances", db.parameters):
        template.add_resource(
            DBInstance(
                f"{db.logical_name}DefaultInstance",
                DBClusterIdentifier=Ref(db.cfn_resource),
                DBInstanceClass="db.t3.medium",
                DBInstanceIdentifier=Ref(AWS_NO_VALUE),
                Tags=Tags(DocDbCluster=Ref(db.cfn_resource)),
            ))
    else:
        for count, instance in enumerate(db.parameters["Instances"]):
            if not isinstance(instance, dict):
                raise TypeError(
                    "Instances definition should be all objects/dict")
            if not keyisset("DBInstanceClass", instance):
                raise KeyError(
                    "You must specify at least the DBInstanceClass",
                    instance.keys(),
                )
            instance_props = import_record_properties(
                instance, DBInstance, ignore_missing_required=True)
            instance_props.update({
                "DBClusterIdentifier":
                Ref(db.cfn_resource),
                "Tags":
                Tags(DocDbCluster=Ref(db.cfn_resource)),
            })
            template.add_resource(
                DBInstance(f"{db.logical_name}Instance{count}",
                           **instance_props))
Beispiel #26
0
def create_from_properties(db_template, db):
    """
    Function to create RDS resources based on the Properties defined in Compose files.
    It will try to identify what type of resource (Cluster or Instance) is defined based on the properties
    that were given. If not capable, falls back to using MacroParameters, and if not, raises exception

    :param troposphere.Template db_template: The template to add the resources to.
    :param ecs_composex.rds.rds_stack.Rds db: The Db object defined in compose.
    :raises: RuntimeError
    """
    rds_class = determine_resource_type(db.name, db.properties)
    if rds_class:
        rds_props = import_record_properties(db.properties, rds_class)
        override_set_properties(rds_props, db)
        db.cfn_resource = rds_class(db.logical_name, **rds_props)
        db_template.add_resource(db.cfn_resource)
    elif db.parameters:
        create_from_parameters(db_template, db)
    else:
        raise RuntimeError(
            f"Failed to identify if {db.logical_name}"
            " is a Cluster or an Instance and MacroParameters are not set."
        )
def create_cluster_from_properties(cluster, template):
    """
    Function to create the Elastic Cache Cluster from properties

    :param ecs_composex.elasticache.elasticache_stack.CacheCluster cluster:
    :param troposphere.Template template:
    :return:
    """
    resource_class = determine_resource_type(cluster.name, cluster.properties)
    props = import_record_properties(cluster.properties, resource_class)
    props["CacheSubnetGroupName"] = Ref(cluster.db_subnet_group)
    handle_security_groups(cluster, props, resource_class)
    default_tags = Tags(Name=cluster.logical_name, ComposeName=cluster.name)
    if keyisset("Tags", props):
        props["Tags"] += default_tags
    else:
        props["Tags"] = default_tags
    if cluster.parameters and keyisset("ParameterGroup", cluster.parameters):
        create_parameter_group(cluster, cluster.parameters["ParameterGroup"])
        template.add_resource(cluster.parameter_group)
        props["CacheParameterGroupName"] = Ref(cluster.parameter_group)
    cluster.cfn_resource = resource_class(cluster.logical_name, **props)
    template.add_resource(cluster.cfn_resource)
Beispiel #28
0
def define_actions(listener, target_def, rule_actions: bool = False) -> list:
    """
    Function to identify the Target definition and create the resulting rule appropriately.

    :param dict target_def:
    :param ecs_composex.elbv2.elbv2_stack.elbv2_listener.ComposeListener listener:
    :param rule_actions: Whether to use Action or ListenerRuleAction
    :return: The action to add or action list for default target
    """
    action_class = Action if not rule_actions else ListenerRuleAction
    if not keyisset("target_arn", target_def):
        raise KeyError("No target ARN defined in the target definition")
    auth_action = None
    actions = []
    if keyisset("AuthenticateCognitoConfig", target_def):
        auth_action_type = "authenticate-cognito"
        props = import_record_properties(
            target_def["AuthenticateCognitoConfig"], AuthenticateCognitoConfig)
        auth_rule = AuthenticateCognitoConfig(**props)
        auth_action = action_class(Type=auth_action_type,
                                   AuthenticateCognitoConfig=auth_rule,
                                   Order=1)
    elif keyisset("AuthenticateOidcConfig", target_def):
        auth_action_type = "authenticate-oidc"
        props = import_record_properties(target_def["AuthenticateOidcConfig"],
                                         AuthenticateOidcConfig)
        auth_rule = AuthenticateOidcConfig(**props)
        auth_action = action_class(Type=auth_action_type,
                                   AuthenticateOidcConfig=auth_rule,
                                   Order=1)
    if auth_action:
        if hasattr(listener, "Certificates") and not listener.Certificates:
            raise AttributeError(
                "In order to use authenticate via OIDC or AWS Cognito,"
                " your listener must be using HTTPs and have SSL Certificates defined."
            )
        if not listener.Protocol == "HTTPS":
            raise AttributeError(
                "In order to use authenticate via OIDC or AWS Cognito,",
                "Your listener protocol MUST be HTTPS. Got",
                listener.Protocol,
            )
        actions.append(auth_action)
        actions.append(
            action_class(
                Type="forward",
                ForwardConfig=ForwardConfig(TargetGroups=[
                    TargetGroupTuple(TargetGroupArn=target_def["target_arn"])
                ]),
                Order=2,
            ))
    else:
        actions.append(
            action_class(
                Type="forward",
                ForwardConfig=ForwardConfig(TargetGroups=[
                    TargetGroupTuple(TargetGroupArn=target_def["target_arn"])
                ]),
                Order=1,
            ))
    return actions
    def __init__(self, mesh_definition, services_root_stack, settings):
        """
        Method to initialize the Mesh

        :param services_root_stack: The services root stack
        :type services_root_stack: ecs_composex.ecs.ServicesStack
        """
        self.nodes = {}
        self.routers = {}
        self.services = {}
        self.routes = []
        self.mesh_settings = mesh_definition["Settings"]
        self.mesh_properties = mesh_definition["Properties"]
        self.stack_parameters = {MESH_NAME.title: Ref(ROOT_STACK_NAME)}
        self.stack = None
        self.appmesh = Ref(MESH_NAME)
        add_parameters(
            services_root_stack.stack_template,
            [appmesh_params.MESH_OWNER_ID, appmesh_params.MESH_NAME],
        )
        appmesh_conditions.add_appmesh_conditions(
            services_root_stack.stack_template)
        if keyisset("MeshName", self.mesh_properties):
            self.mesh_name = self.mesh_properties["MeshName"]
        else:
            self.mesh_name = MESH_NAME.Default

        mesh_info = lookup_mesh_by_name(
            session=settings.session,
            mesh_name=self.mesh_name,
            mesh_owner=str(self.mesh_properties["MeshOwner"]) if keyisset(
                "MeshOwner", self.mesh_properties) else None,
        )
        if mesh_info:
            services_root_stack.Parameters.update({
                appmesh_params.MESH_NAME_T:
                mesh_info[MESH_NAME.title],
                appmesh_params.MESH_OWNER_ID_T:
                mesh_info[MESH_OWNER_ID.title],
            })
        else:
            if self.mesh_properties:
                props = import_record_properties(self.mesh_properties,
                                                 appmesh.Mesh)
                props["Metadata"] = metadata
                props["MeshName"] = appmesh_conditions.set_mesh_name()
            else:
                props = {
                    "Spec":
                    appmesh.MeshSpec(EgressFilter=appmesh.EgressFilter(
                        Type="DENY_ALL")),
                    "MeshName":
                    appmesh_conditions.set_mesh_name(),
                }
            self.appmesh = appmesh.Mesh(
                self.mesh_title,
                template=services_root_stack.stack_template,
                **props,
            )
            services_root_stack.Parameters.update({
                appmesh_params.MESH_NAME_T:
                Ref(AWS_STACK_NAME),
                appmesh_params.MESH_OWNER_ID_T:
                Ref(AWS_ACCOUNT_ID),
            })
        for key in self.required_keys:
            if key not in self.mesh_settings.keys():
                raise KeyError(
                    f"Key {key} is missing. Required {self.required_keys}")
        self.define_nodes(settings, services_root_stack, self.appmesh)
        self.define_routes_and_routers()
        self.define_virtual_services(settings)