Source code for ecs_composex.ecs_cluster.ecs_family_helpers

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

from __future__ import annotations

from typing import TYPE_CHECKING, Union

if TYPE_CHECKING:
    from ecs_composex.common.settings import ComposeXSettings
    from ecs_composex.ecs.ecs_family import ComposeFamily
    from ecs_composex.ecs_cluster import EcsCluster

from troposphere import NoValue

from ecs_composex.ecs.ecs_params import LAUNCH_TYPE
from ecs_composex.ecs_cluster import FARGATE_PROVIDERS
from ecs_composex.ecs_composex import LOG

"""
Module to set the Launch Type / Capacity providers of ComposeFamily according to the ECS Cluter settings
and x-ecs settings
"""


[docs]def validate_capacity_providers(family: ComposeFamily, cluster: EcsCluster) -> bool: """ Validates that the defined ecs_capacity_providers are all available in the ECS Cluster Providers :raises: ValueError if not all task family providers in the cluster providers :raises: TypeError if cluster_providers not a list """ if ( not family.service_compute.ecs_capacity_providers and not cluster.capacity_providers ): LOG.debug( f"{family.name} - No capacity providers specified in task definition nor cluster" ) return True elif not cluster.capacity_providers: LOG.debug(f"{family.name} - No capacity provider set for cluster") return True cap_names = [ cap.CapacityProvider for cap in family.service_compute.ecs_capacity_providers ] if not all(cap_name in FARGATE_PROVIDERS for cap_name in cap_names): raise ValueError( f"{family.name} - You cannot mix FARGATE capacity provider with AutoScaling Capacity Providers", cap_names, ) if not isinstance(cluster.capacity_providers, list): raise TypeError("clusters_providers must be a list") elif not all(provider in cluster.capacity_providers for provider in cap_names): raise ValueError( "Providers", cap_names, "not defined in ECS Cluster providers. Available providers are", cluster.capacity_providers, )
[docs]def validate_compute_configuration_for_task( family: ComposeFamily, settings: ComposeXSettings ) -> None: """Function to perform a final validation of compute before rendering.""" if ( family.service_compute.launch_type and family.service_compute.launch_type == "EXTERNAL" ): LOG.debug(f"{family.name} - Launch Type set to EXTERNAL. Nothing to do.") return if settings.ecs_cluster.platform_override: family.service_compute.launch_type = settings.ecs_cluster.platform_override LOG.warning( f"{family.name} - Due to Launch Type override to {settings.ecs_cluster.platform_override}" ", ignoring CapacityProviders" f"{[_cap.CapacityProvider for _cap in family.service_compute.ecs_capacity_providers]}" ) if family.service_definition: setattr( family.service_definition, "CapacityProviderStrategy", NoValue, ) else: family.service_compute.set_update_launch_type() family.service_compute.set_update_capacity_providers() validate_capacity_providers(family, settings.ecs_cluster) if ( not family.service_compute.ecs_capacity_providers and family.service_compute.launch_type in ["EC2", "EXTERNAL"] ): return set_service_launch_type(family, settings.ecs_cluster) LOG.debug( f"{family.name} - Updated {LAUNCH_TYPE.title} to" f" {family.service_compute.launch_type}" )
[docs]def set_launch_type_from_cluster_and_service( family: ComposeFamily, cluster: EcsCluster ) -> None: """ Sets the launch type based on the service and capacity providers If all the capacity providers of the service are FARGATE, we use `FARGATE_PROVIDERS` which removes `LaunchType` from ECS Service definition Otherwise, we use the capacity providers set which use AutoScaling. """ family_providers: list = [ cap.CapacityProvider for cap in family.service_compute.ecs_capacity_providers ] family_uses_fargate_only = all( provider in FARGATE_PROVIDERS for provider in family_providers ) cluster_uses_fargate_only = all( provider in FARGATE_PROVIDERS for provider in cluster.capacity_providers ) if not all(provider in cluster.capacity_providers for provider in family_providers): raise AttributeError( "Family {} tries to use providers not available in the cluster. " "Wants: {}. Available: {}".format( family.name, family_providers, cluster.capacity_providers ) ) if family_uses_fargate_only and cluster_uses_fargate_only: family.service_compute.launch_type = "FARGATE_PROVIDERS" else: family.service_compute.launch_type = "SERVICE_MODE" LOG.info( f"{family.name} - Using AutoScaling Based Providers", [ provider.CapacityProvider for provider in family.service_compute.ecs_capacity_providers ], )
[docs]def set_launch_type_from_cluster_only( family: ComposeFamily, cluster: EcsCluster ) -> None: """ When the family x-ecs has not set CapacityProviders, we rely on the ECS Cluster definition. If all the capacity providers defined on the Cluster are FARGATE related, use `FARGATE_PROVIDERS` Otherwise, use the ECS Cluster defined capacity providers based on the Cluster strategy. """ if any( provider in ["FARGATE", "FARGATE_SPOT"] for provider in cluster.default_strategy_providers ) or all( provider in cluster.capacity_providers for provider in ["FARGATE", "FARGATE_SPOT"] ): family.service_compute.launch_type = "FARGATE_PROVIDERS" LOG.debug( f"{family.name} - Defaulting to FARGATE_PROVIDERS as " "FARGATE[_SPOT] is found in the cluster default strategy" ) else: family.service_compute.launch_type = "CLUSTER_MODE" LOG.debug( f"{family.name} - Cluster uses non Fargate Capacity Providers. Setting to Cluster default" ) family.service_compute.launch_type = "CLUSTER_MODE"
[docs]def set_service_launch_type(family: ComposeFamily, cluster) -> None: """ Sets the LaunchType value for the ECS Service If the LaunchType is EXTERNAL or EC2, we ignore Capacity Providers altogether. """ if family.service_compute.launch_type in ["EXTERNAL", "EC2"]: LOG.debug( "services.{} uses {}. Skipping".format( family.name, family.service_compute.launch_type ) ) return if family.service_compute.ecs_capacity_providers and cluster.capacity_providers: set_launch_type_from_cluster_and_service(family, cluster) elif ( not family.service_compute.ecs_capacity_providers and cluster.capacity_providers ): set_launch_type_from_cluster_only(family, cluster)