2017-07-20 22:00:30 +00:00
import datetime
2017-08-18 15:16:11 +00:00
import re
2019-08-10 04:34:52 +00:00
from jinja2 import Template
2019-09-12 16:29:03 +00:00
from botocore . exceptions import ParamValidationError
2021-08-04 16:24:26 +00:00
from collections import OrderedDict
2017-10-29 14:14:17 +00:00
from moto . core . exceptions import RESTError
2020-08-01 14:23:36 +00:00
from moto . core import BaseBackend , BaseModel , CloudFormationModel
2021-01-31 11:39:11 +00:00
from moto . core . utils import (
iso_8601_datetime_with_milliseconds ,
2021-06-10 06:02:30 +00:00
get_random_hex ,
2021-01-31 11:39:11 +00:00
)
2017-07-20 22:00:30 +00:00
from moto . ec2 . models import ec2_backends
2017-10-29 14:14:17 +00:00
from moto . acm . models import acm_backends
2017-11-06 22:17:10 +00:00
from . utils import make_arn_for_target_group
from . utils import make_arn_for_load_balancer
2017-07-20 22:00:30 +00:00
from . exceptions import (
DuplicateLoadBalancerName ,
DuplicateListenerError ,
DuplicateTargetGroupName ,
InvalidTargetError ,
ListenerNotFoundError ,
LoadBalancerNotFoundError ,
SubnetNotFoundError ,
TargetGroupNotFoundError ,
TooManyTagsError ,
2017-08-16 12:09:14 +00:00
PriorityInUseError ,
InvalidConditionFieldError ,
InvalidConditionValueError ,
InvalidActionTypeError ,
ActionTargetGroupNotFoundError ,
2017-08-16 17:25:39 +00:00
InvalidDescribeRulesRequest ,
2017-10-02 19:35:52 +00:00
ResourceInUseError ,
2017-08-16 18:10:26 +00:00
RuleNotFoundError ,
2017-08-18 14:54:28 +00:00
DuplicatePriorityError ,
2017-08-21 19:28:58 +00:00
InvalidTargetGroupNameError ,
2019-09-12 16:29:03 +00:00
InvalidModifyRuleArgumentsError ,
2019-10-31 15:44:26 +00:00
InvalidStatusCodeActionTypeError ,
InvalidLoadBalancerActionException ,
)
2017-07-20 22:00:30 +00:00
class FakeHealthStatus ( BaseModel ) :
2019-10-31 15:44:26 +00:00
def __init__ (
self , instance_id , port , health_port , status , reason = None , description = None
) :
2017-07-20 22:00:30 +00:00
self . instance_id = instance_id
self . port = port
self . health_port = health_port
self . status = status
self . reason = reason
2019-07-18 14:57:27 +00:00
self . description = description
2017-07-20 22:00:30 +00:00
2020-08-01 14:23:36 +00:00
class FakeTargetGroup ( CloudFormationModel ) :
2019-10-31 15:44:26 +00:00
HTTP_CODE_REGEX = re . compile ( r " (?:(?: \ d+- \ d+| \ d+),?)+ " )
def __init__ (
self ,
name ,
arn ,
vpc_id ,
protocol ,
port ,
healthcheck_protocol = None ,
healthcheck_port = None ,
healthcheck_path = None ,
healthcheck_interval_seconds = None ,
healthcheck_timeout_seconds = None ,
2021-07-26 14:21:17 +00:00
healthcheck_enabled = None ,
2019-10-31 15:44:26 +00:00
healthy_threshold_count = None ,
unhealthy_threshold_count = None ,
matcher = None ,
target_type = None ,
) :
2017-11-20 11:18:21 +00:00
# TODO: default values differs when you add Network Load balancer
2017-07-20 22:00:30 +00:00
self . name = name
self . arn = arn
self . vpc_id = vpc_id
self . protocol = protocol
self . port = port
2021-07-26 14:21:17 +00:00
self . healthcheck_protocol = healthcheck_protocol or self . protocol
self . healthcheck_port = healthcheck_port
self . healthcheck_path = healthcheck_path
2017-11-20 11:18:21 +00:00
self . healthcheck_interval_seconds = healthcheck_interval_seconds or 30
self . healthcheck_timeout_seconds = healthcheck_timeout_seconds or 5
2021-07-26 14:21:17 +00:00
self . healthcheck_enabled = healthcheck_enabled
2017-11-20 11:18:21 +00:00
self . healthy_threshold_count = healthy_threshold_count or 5
self . unhealthy_threshold_count = unhealthy_threshold_count or 2
2017-07-20 22:00:30 +00:00
self . load_balancer_arns = [ ]
2017-09-17 02:53:09 +00:00
self . tags = { }
2021-07-26 14:21:17 +00:00
self . matcher = matcher
if self . protocol != " TCP " :
self . matcher = self . matcher or { " HttpCode " : " 200 " }
self . healthcheck_path = self . healthcheck_path or " / "
self . healthcheck_port = self . healthcheck_port or str ( self . port )
2017-10-27 17:25:22 +00:00
self . target_type = target_type
2017-07-20 22:00:30 +00:00
2017-08-02 22:57:15 +00:00
self . attributes = {
2019-10-31 15:44:26 +00:00
" deregistration_delay.timeout_seconds " : 300 ,
" stickiness.enabled " : " false " ,
2017-08-02 22:57:15 +00:00
}
2017-07-20 22:00:30 +00:00
self . targets = OrderedDict ( )
2017-10-27 18:24:45 +00:00
@property
def physical_resource_id ( self ) :
return self . arn
2017-07-20 22:00:30 +00:00
def register ( self , targets ) :
for target in targets :
2019-10-31 15:44:26 +00:00
self . targets [ target [ " id " ] ] = {
" id " : target [ " id " ] ,
" port " : target . get ( " port " , self . port ) ,
2017-07-20 22:00:30 +00:00
}
def deregister ( self , targets ) :
for target in targets :
2019-10-31 15:44:26 +00:00
t = self . targets . pop ( target [ " id " ] , None )
2017-07-20 22:00:30 +00:00
if not t :
raise InvalidTargetError ( )
2019-08-30 16:21:11 +00:00
def deregister_terminated_instances ( self , instance_ids ) :
for target_id in list ( self . targets . keys ( ) ) :
if target_id in instance_ids :
del self . targets [ target_id ]
2017-09-17 02:53:09 +00:00
def add_tag ( self , key , value ) :
if len ( self . tags ) > = 10 and key not in self . tags :
raise TooManyTagsError ( )
self . tags [ key ] = value
2019-07-18 14:57:27 +00:00
def health_for ( self , target , ec2_backend ) :
2019-10-31 15:44:26 +00:00
t = self . targets . get ( target [ " id " ] )
2017-07-20 22:00:30 +00:00
if t is None :
raise InvalidTargetError ( )
2019-10-31 15:44:26 +00:00
if t [ " id " ] . startswith ( " i- " ) : # EC2 instance ID
instance = ec2_backend . get_instance_by_id ( t [ " id " ] )
2019-07-18 14:57:27 +00:00
if instance . state == " stopped " :
2019-10-31 15:44:26 +00:00
return FakeHealthStatus (
t [ " id " ] ,
t [ " port " ] ,
self . healthcheck_port ,
" unused " ,
" Target.InvalidState " ,
" Target is in the stopped state " ,
)
return FakeHealthStatus ( t [ " id " ] , t [ " port " ] , self . healthcheck_port , " healthy " )
2017-07-20 22:00:30 +00:00
2020-08-01 14:23:36 +00:00
@staticmethod
def cloudformation_name_type ( ) :
return " Name "
@staticmethod
def cloudformation_type ( ) :
# https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-elasticloadbalancingv2-targetgroup.html
return " AWS::ElasticLoadBalancingV2::TargetGroup "
2017-10-27 17:25:22 +00:00
@classmethod
2019-10-31 15:44:26 +00:00
def create_from_cloudformation_json (
2021-11-03 21:00:42 +00:00
cls , resource_name , cloudformation_json , region_name , * * kwargs
2019-10-31 15:44:26 +00:00
) :
properties = cloudformation_json [ " Properties " ]
2017-10-27 17:25:22 +00:00
elbv2_backend = elbv2_backends [ region_name ]
vpc_id = properties . get ( " VpcId " )
2019-10-31 15:44:26 +00:00
protocol = properties . get ( " Protocol " )
2017-10-27 17:25:22 +00:00
port = properties . get ( " Port " )
healthcheck_protocol = properties . get ( " HealthCheckProtocol " )
healthcheck_port = properties . get ( " HealthCheckPort " )
healthcheck_path = properties . get ( " HealthCheckPath " )
healthcheck_interval_seconds = properties . get ( " HealthCheckIntervalSeconds " )
healthcheck_timeout_seconds = properties . get ( " HealthCheckTimeoutSeconds " )
healthy_threshold_count = properties . get ( " HealthyThresholdCount " )
unhealthy_threshold_count = properties . get ( " UnhealthyThresholdCount " )
matcher = properties . get ( " Matcher " )
target_type = properties . get ( " TargetType " )
target_group = elbv2_backend . create_target_group (
2020-08-27 09:11:47 +00:00
name = resource_name ,
2017-10-27 17:25:22 +00:00
vpc_id = vpc_id ,
protocol = protocol ,
port = port ,
healthcheck_protocol = healthcheck_protocol ,
healthcheck_port = healthcheck_port ,
healthcheck_path = healthcheck_path ,
healthcheck_interval_seconds = healthcheck_interval_seconds ,
healthcheck_timeout_seconds = healthcheck_timeout_seconds ,
healthy_threshold_count = healthy_threshold_count ,
unhealthy_threshold_count = unhealthy_threshold_count ,
matcher = matcher ,
target_type = target_type ,
)
return target_group
2017-07-20 22:00:30 +00:00
2020-08-01 14:23:36 +00:00
class FakeListener ( CloudFormationModel ) :
2019-10-31 15:44:26 +00:00
def __init__ (
self ,
load_balancer_arn ,
arn ,
protocol ,
port ,
ssl_policy ,
certificate ,
default_actions ,
) :
2017-07-20 22:00:30 +00:00
self . load_balancer_arn = load_balancer_arn
self . arn = arn
2021-07-26 14:21:17 +00:00
self . protocol = ( protocol or " " ) . upper ( )
2017-07-20 22:00:30 +00:00
self . port = port
self . ssl_policy = ssl_policy
self . certificate = certificate
2017-10-29 14:14:17 +00:00
self . certificates = [ certificate ] if certificate is not None else [ ]
2017-07-20 22:00:30 +00:00
self . default_actions = default_actions
2021-06-05 09:04:04 +00:00
self . _non_default_rules = OrderedDict ( )
self . _default_rule = OrderedDict ( )
self . _default_rule [ 0 ] = FakeRule (
2017-08-16 12:09:14 +00:00
listener_arn = self . arn ,
conditions = [ ] ,
2019-10-31 15:44:26 +00:00
priority = " default " ,
2017-08-16 12:09:14 +00:00
actions = default_actions ,
2019-10-31 15:44:26 +00:00
is_default = True ,
2017-08-16 12:09:14 +00:00
)
2021-07-26 14:21:17 +00:00
self . tags = { }
2017-08-16 12:09:14 +00:00
2017-10-27 18:24:45 +00:00
@property
def physical_resource_id ( self ) :
return self . arn
2017-08-16 12:09:14 +00:00
@property
def rules ( self ) :
2021-06-05 09:04:04 +00:00
return OrderedDict (
list ( self . _non_default_rules . items ( ) ) + list ( self . _default_rule . items ( ) )
)
2017-08-16 12:09:14 +00:00
2021-06-05 09:04:04 +00:00
def remove_rule ( self , arn ) :
self . _non_default_rules . pop ( arn )
2017-08-21 19:30:03 +00:00
2021-06-05 09:04:04 +00:00
def register ( self , arn , rule ) :
self . _non_default_rules [ arn ] = rule
sorted ( self . _non_default_rules . values ( ) , key = lambda x : x . priority )
2017-08-16 12:09:14 +00:00
2020-08-01 14:23:36 +00:00
@staticmethod
def cloudformation_name_type ( ) :
return None
@staticmethod
def cloudformation_type ( ) :
# https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-elasticloadbalancingv2-listener.html
return " AWS::ElasticLoadBalancingV2::Listener "
2017-10-27 18:24:45 +00:00
@classmethod
2019-10-31 15:44:26 +00:00
def create_from_cloudformation_json (
2021-11-03 21:00:42 +00:00
cls , resource_name , cloudformation_json , region_name , * * kwargs
2019-10-31 15:44:26 +00:00
) :
properties = cloudformation_json [ " Properties " ]
2017-10-27 18:24:45 +00:00
elbv2_backend = elbv2_backends [ region_name ]
load_balancer_arn = properties . get ( " LoadBalancerArn " )
protocol = properties . get ( " Protocol " )
port = properties . get ( " Port " )
ssl_policy = properties . get ( " SslPolicy " )
certificates = properties . get ( " Certificates " )
2021-06-11 20:56:28 +00:00
default_actions = elbv2_backend . convert_and_validate_properties ( properties )
2021-07-01 15:25:40 +00:00
certificates = elbv2_backend . convert_and_validate_certificates ( certificates )
2017-10-27 18:24:45 +00:00
listener = elbv2_backend . create_listener (
2019-10-31 15:44:26 +00:00
load_balancer_arn , protocol , port , ssl_policy , certificates , default_actions
)
2017-10-27 18:24:45 +00:00
return listener
2021-06-11 20:56:28 +00:00
@classmethod
def update_from_cloudformation_json (
cls , original_resource , new_resource_name , cloudformation_json , region_name
) :
properties = cloudformation_json [ " Properties " ]
elbv2_backend = elbv2_backends [ region_name ]
protocol = properties . get ( " Protocol " )
port = properties . get ( " Port " )
ssl_policy = properties . get ( " SslPolicy " )
certificates = properties . get ( " Certificates " )
default_actions = elbv2_backend . convert_and_validate_properties ( properties )
2021-07-01 15:25:40 +00:00
certificates = elbv2_backend . convert_and_validate_certificates ( certificates )
2021-06-11 20:56:28 +00:00
listener = elbv2_backend . modify_listener (
original_resource . arn ,
port ,
protocol ,
ssl_policy ,
certificates ,
default_actions ,
)
return listener
2017-08-16 12:09:14 +00:00
2021-06-05 09:04:04 +00:00
class FakeListenerRule ( CloudFormationModel ) :
def __init__ (
self , listener_arn , arn , conditions , priority , actions ,
) :
self . listener_arn = listener_arn
self . arn = arn
self . conditions = conditions
self . actions = actions
self . priority = priority
@property
def physical_resource_id ( self ) :
return self . arn
@staticmethod
def cloudformation_type ( ) :
# https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-elasticloadbalancingv2-listenerrule.html
return " AWS::ElasticLoadBalancingV2::ListenerRule "
@classmethod
def create_from_cloudformation_json (
2021-11-03 21:00:42 +00:00
cls , resource_name , cloudformation_json , region_name , * * kwargs
2021-06-05 09:04:04 +00:00
) :
properties = cloudformation_json [ " Properties " ]
elbv2_backend = elbv2_backends [ region_name ]
listener_arn = properties . get ( " ListenerArn " )
priority = properties . get ( " Priority " )
conditions = properties . get ( " Conditions " )
2021-06-11 20:56:28 +00:00
actions = elbv2_backend . convert_and_validate_action_properties ( properties )
2021-06-05 09:04:04 +00:00
listener_rule = elbv2_backend . create_rule (
2021-06-11 20:56:28 +00:00
listener_arn , conditions , priority , actions
)
return listener_rule
@classmethod
def update_from_cloudformation_json (
cls , original_resource , new_resource_name , cloudformation_json , region_name
) :
properties = cloudformation_json [ " Properties " ]
elbv2_backend = elbv2_backends [ region_name ]
conditions = properties . get ( " Conditions " )
actions = elbv2_backend . convert_and_validate_action_properties ( properties )
listener_rule = elbv2_backend . modify_rule (
original_resource . arn , conditions , actions
2021-06-05 09:04:04 +00:00
)
return listener_rule
class FakeRule ( BaseModel ) :
def __init__ ( self , listener_arn , conditions , priority , actions , is_default ) :
self . listener_arn = listener_arn
self . arn = listener_arn . replace ( " :listener/ " , " :listener-rule/ " ) + " / %s " % (
id ( self )
)
self . conditions = conditions
self . priority = priority # int or 'default'
self . actions = actions
self . is_default = is_default
2019-08-10 04:34:52 +00:00
class FakeAction ( BaseModel ) :
def __init__ ( self , data ) :
self . data = data
2021-10-11 21:12:38 +00:00
self . type = data . get ( " Type " )
2019-08-10 04:34:52 +00:00
def to_xml ( self ) :
2019-10-31 15:44:26 +00:00
template = Template (
""" <Type> {{ action.type }}</Type>
2021-10-11 21:12:38 +00:00
{ % if action . type == " forward " and " ForwardConfig " in action . data % }
2021-06-09 17:41:18 +00:00
< ForwardConfig >
< TargetGroups >
2021-10-11 21:12:38 +00:00
{ % for target_group in action . data [ " ForwardConfig " ] [ " TargetGroups " ] % }
2021-06-09 17:41:18 +00:00
< member >
2021-10-11 21:12:38 +00:00
< TargetGroupArn > { { target_group [ " TargetGroupArn " ] } } < / TargetGroupArn >
< Weight > { { target_group [ " Weight " ] } } < / Weight >
2021-06-09 17:41:18 +00:00
< / member >
{ % endfor % }
< / TargetGroups >
< / ForwardConfig >
{ % endif % }
2021-10-11 21:12:38 +00:00
{ % if action . type == " forward " and " ForwardConfig " not in action . data % }
< TargetGroupArn > { { action . data [ " TargetGroupArn " ] } } < / TargetGroupArn >
2019-08-10 04:34:52 +00:00
{ % elif action . type == " redirect " % }
< RedirectConfig >
2021-10-11 21:12:38 +00:00
< Protocol > { { action . data [ " RedirectConfig " ] [ " Protocol " ] } } < / Protocol >
< Port > { { action . data [ " RedirectConfig " ] [ " Port " ] } } < / Port >
< StatusCode > { { action . data [ " RedirectConfig " ] [ " StatusCode " ] } } < / StatusCode >
2019-08-10 04:34:52 +00:00
< / RedirectConfig >
{ % elif action . type == " authenticate-cognito " % }
< AuthenticateCognitoConfig >
2021-10-11 21:12:38 +00:00
< UserPoolArn > { { action . data [ " AuthenticateCognitoConfig " ] [ " UserPoolArn " ] } } < / UserPoolArn >
< UserPoolClientId > { { action . data [ " AuthenticateCognitoConfig " ] [ " UserPoolClientId " ] } } < / UserPoolClientId >
< UserPoolDomain > { { action . data [ " AuthenticateCognitoConfig " ] [ " UserPoolDomain " ] } } < / UserPoolDomain >
2019-08-10 04:34:52 +00:00
< / AuthenticateCognitoConfig >
2019-09-12 15:24:47 +00:00
{ % elif action . type == " fixed-response " % }
< FixedResponseConfig >
2021-10-11 21:12:38 +00:00
< ContentType > { { action . data [ " FixedResponseConfig " ] [ " ContentType " ] } } < / ContentType >
< MessageBody > { { action . data [ " FixedResponseConfig " ] [ " MessageBody " ] } } < / MessageBody >
< StatusCode > { { action . data [ " FixedResponseConfig " ] [ " StatusCode " ] } } < / StatusCode >
2019-09-12 15:24:47 +00:00
< / FixedResponseConfig >
2019-08-10 04:34:52 +00:00
{ % endif % }
2019-10-31 15:44:26 +00:00
"""
)
2019-08-10 04:34:52 +00:00
return template . render ( action = self )
2017-07-20 22:00:30 +00:00
class FakeBackend ( BaseModel ) :
def __init__ ( self , instance_port ) :
self . instance_port = instance_port
self . policy_names = [ ]
def __repr__ ( self ) :
2019-10-31 15:44:26 +00:00
return " FakeBackend(inp: %s , policies: %s ) " % (
self . instance_port ,
self . policy_names ,
)
2017-07-20 22:00:30 +00:00
2020-08-01 14:23:36 +00:00
class FakeLoadBalancer ( CloudFormationModel ) :
2019-10-31 15:44:26 +00:00
VALID_ATTRS = {
" access_logs.s3.enabled " ,
" access_logs.s3.bucket " ,
" access_logs.s3.prefix " ,
" deletion_protection.enabled " ,
" idle_timeout.timeout_seconds " ,
2021-11-06 12:37:19 +00:00
" routing.http2.enabled " ,
" routing.http.drop_invalid_header_fields.enabled " ,
2019-10-31 15:44:26 +00:00
}
def __init__ (
self ,
name ,
security_groups ,
subnets ,
vpc_id ,
arn ,
dns_name ,
2021-05-18 06:52:39 +00:00
state ,
2019-10-31 15:44:26 +00:00
scheme = " internet-facing " ,
2021-07-26 14:21:17 +00:00
loadbalancer_type = None ,
2019-10-31 15:44:26 +00:00
) :
2017-07-20 22:00:30 +00:00
self . name = name
2021-01-31 11:39:11 +00:00
self . created_time = iso_8601_datetime_with_milliseconds ( datetime . datetime . now ( ) )
2017-07-20 22:00:30 +00:00
self . scheme = scheme
self . security_groups = security_groups
self . subnets = subnets or [ ]
self . vpc_id = vpc_id
self . listeners = OrderedDict ( )
self . tags = { }
self . arn = arn
self . dns_name = dns_name
2021-05-18 06:52:39 +00:00
self . state = state
2021-07-26 14:21:17 +00:00
self . loadbalancer_type = loadbalancer_type or " application "
2017-07-20 22:00:30 +00:00
2019-10-31 15:44:26 +00:00
self . stack = " ipv4 "
2017-10-29 14:14:17 +00:00
self . attrs = {
2021-07-26 14:21:17 +00:00
# "access_logs.s3.enabled": "false", # commented out for TF compatibility
2019-10-31 15:44:26 +00:00
" access_logs.s3.bucket " : None ,
" access_logs.s3.prefix " : None ,
" deletion_protection.enabled " : " false " ,
2021-07-26 14:21:17 +00:00
# "idle_timeout.timeout_seconds": "60", # commented out for TF compatibility
2017-10-29 14:14:17 +00:00
}
2017-07-20 22:00:30 +00:00
@property
def physical_resource_id ( self ) :
2017-10-27 18:24:45 +00:00
return self . arn
2017-07-20 22:00:30 +00:00
def add_tag ( self , key , value ) :
if len ( self . tags ) > = 10 and key not in self . tags :
raise TooManyTagsError ( )
self . tags [ key ] = value
def list_tags ( self ) :
return self . tags
def remove_tag ( self , key ) :
if key in self . tags :
del self . tags [ key ]
2021-05-18 06:52:39 +00:00
def activate ( self ) :
if self . state == " provisioning " :
self . state = " active "
2017-07-20 22:00:30 +00:00
def delete ( self , region ) :
2021-06-23 15:57:09 +00:00
""" Not exposed as part of the ELB API - used for CloudFormation. """
2017-07-20 22:00:30 +00:00
elbv2_backends [ region ] . delete_load_balancer ( self . arn )
2020-08-01 14:23:36 +00:00
@staticmethod
def cloudformation_name_type ( ) :
return " Name "
@staticmethod
def cloudformation_type ( ) :
# https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-elasticloadbalancingv2-loadbalancer.html
return " AWS::ElasticLoadBalancingV2::LoadBalancer "
2017-10-27 15:54:43 +00:00
@classmethod
2019-10-31 15:44:26 +00:00
def create_from_cloudformation_json (
2021-11-03 21:00:42 +00:00
cls , resource_name , cloudformation_json , region_name , * * kwargs
2019-10-31 15:44:26 +00:00
) :
properties = cloudformation_json [ " Properties " ]
2017-10-27 15:54:43 +00:00
elbv2_backend = elbv2_backends [ region_name ]
security_groups = properties . get ( " SecurityGroups " )
2019-10-31 15:44:26 +00:00
subnet_ids = properties . get ( " Subnets " )
scheme = properties . get ( " Scheme " , " internet-facing " )
2017-10-27 15:54:43 +00:00
2019-10-31 15:44:26 +00:00
load_balancer = elbv2_backend . create_load_balancer (
2020-08-27 09:11:47 +00:00
resource_name , security_groups , subnet_ids , scheme = scheme
2019-10-31 15:44:26 +00:00
)
2017-10-27 15:54:43 +00:00
return load_balancer
2021-11-03 21:00:42 +00:00
@classmethod
def has_cfn_attr ( cls , attribute ) :
return attribute in [
" DNSName " ,
" LoadBalancerName " ,
" CanonicalHostedZoneID " ,
" LoadBalancerFullName " ,
" SecurityGroups " ,
]
2017-10-27 19:32:16 +00:00
def get_cfn_attribute ( self , attribute_name ) :
2019-10-31 15:44:26 +00:00
"""
2017-11-15 15:11:11 +00:00
Implemented attributes :
* DNSName
* LoadBalancerName
Not implemented :
* CanonicalHostedZoneID
* LoadBalancerFullName
* SecurityGroups
This method is similar to models . py : FakeLoadBalancer . get_cfn_attribute ( )
2019-10-31 15:44:26 +00:00
"""
2017-11-15 15:11:11 +00:00
from moto . cloudformation . exceptions import UnformattedGetAttTemplateException
2019-10-31 15:44:26 +00:00
2017-11-15 15:11:11 +00:00
not_implemented_yet = [
2019-10-31 15:44:26 +00:00
" CanonicalHostedZoneID " ,
" LoadBalancerFullName " ,
" SecurityGroups " ,
2017-11-15 15:11:11 +00:00
]
2019-10-31 15:44:26 +00:00
if attribute_name == " DNSName " :
2017-11-15 15:11:11 +00:00
return self . dns_name
2019-10-31 15:44:26 +00:00
elif attribute_name == " LoadBalancerName " :
2017-11-15 15:11:11 +00:00
return self . name
elif attribute_name in not_implemented_yet :
2019-10-31 15:44:26 +00:00
raise NotImplementedError (
' " Fn::GetAtt " : [ " {0} " , " %s " ] " ' % attribute_name
)
2017-11-15 15:11:11 +00:00
else :
raise UnformattedGetAttTemplateException ( )
2017-07-20 22:00:30 +00:00
class ELBv2Backend ( BaseBackend ) :
def __init__ ( self , region_name = None ) :
self . region_name = region_name
self . target_groups = OrderedDict ( )
self . load_balancers = OrderedDict ( )
2021-09-24 16:01:09 +00:00
@staticmethod
def default_vpc_endpoint_service ( service_region , zones ) :
""" Default VPC endpoint service. """
return BaseBackend . default_vpc_endpoint_service_factory (
service_region , zones , " elasticloadbalancing "
)
2017-10-29 14:14:17 +00:00
@property
def ec2_backend ( self ) :
"""
EC2 backend
: return : EC2 Backend
: rtype : moto . ec2 . models . EC2Backend
"""
return ec2_backends [ self . region_name ]
@property
def acm_backend ( self ) :
"""
ACM backend
: return : ACM Backend
: rtype : moto . acm . models . AWSCertificateManagerBackend
"""
return acm_backends [ self . region_name ]
2017-07-20 22:00:30 +00:00
def reset ( self ) :
region_name = self . region_name
self . __dict__ = { }
self . __init__ ( region_name )
2019-10-31 15:44:26 +00:00
def create_load_balancer (
2021-07-26 14:21:17 +00:00
self ,
name ,
security_groups ,
subnet_ids ,
scheme = " internet-facing " ,
loadbalancer_type = None ,
2019-10-31 15:44:26 +00:00
) :
2017-07-20 22:00:30 +00:00
vpc_id = None
subnets = [ ]
2021-05-18 06:52:39 +00:00
state = " provisioning "
2017-07-20 22:00:30 +00:00
if not subnet_ids :
raise SubnetNotFoundError ( )
for subnet_id in subnet_ids :
2017-10-29 14:14:17 +00:00
subnet = self . ec2_backend . get_subnet ( subnet_id )
2017-07-20 22:00:30 +00:00
if subnet is None :
raise SubnetNotFoundError ( )
subnets . append ( subnet )
vpc_id = subnets [ 0 ] . vpc_id
2019-10-31 15:44:26 +00:00
arn = make_arn_for_load_balancer (
account_id = 1 , name = name , region_name = self . region_name
)
2017-07-20 22:00:30 +00:00
dns_name = " %s -1. %s .elb.amazonaws.com " % ( name , self . region_name )
if arn in self . load_balancers :
raise DuplicateLoadBalancerName ( )
new_load_balancer = FakeLoadBalancer (
name = name ,
security_groups = security_groups ,
arn = arn ,
scheme = scheme ,
subnets = subnets ,
vpc_id = vpc_id ,
2019-10-31 15:44:26 +00:00
dns_name = dns_name ,
2021-05-18 06:52:39 +00:00
state = state ,
2021-07-26 14:21:17 +00:00
loadbalancer_type = loadbalancer_type ,
2019-10-31 15:44:26 +00:00
)
2017-07-20 22:00:30 +00:00
self . load_balancers [ arn ] = new_load_balancer
return new_load_balancer
2021-06-11 20:56:28 +00:00
def convert_and_validate_action_properties ( self , properties ) :
# transform Actions to confirm with the rest of the code and XML templates
default_actions = [ ]
for i , action in enumerate ( properties [ " Actions " ] ) :
action_type = action [ " Type " ]
2021-10-11 21:12:38 +00:00
if action_type in [
2021-06-11 20:56:28 +00:00
" redirect " ,
" authenticate-cognito " ,
" fixed-response " ,
2021-10-11 21:12:38 +00:00
" forward " ,
2021-06-11 20:56:28 +00:00
] :
2021-10-11 21:12:38 +00:00
default_actions . append ( action )
2021-06-11 20:56:28 +00:00
else :
raise InvalidActionTypeError ( action_type , i + 1 )
return default_actions
2017-08-16 12:09:14 +00:00
def create_rule ( self , listener_arn , conditions , priority , actions ) :
2019-08-10 04:34:52 +00:00
actions = [ FakeAction ( action ) for action in actions ]
2017-08-16 12:09:14 +00:00
listeners = self . describe_listeners ( None , [ listener_arn ] )
if not listeners :
2017-08-16 18:22:40 +00:00
raise ListenerNotFoundError ( )
2017-08-16 12:09:14 +00:00
listener = listeners [ 0 ]
# validate conditions
2021-07-26 14:21:17 +00:00
# see: https://docs.aws.amazon.com/cli/latest/reference/elbv2/create-rule.html
2021-07-16 07:01:14 +00:00
self . _validate_conditions ( conditions )
2021-06-05 09:04:04 +00:00
2021-07-26 14:21:17 +00:00
# TODO: check QueryStringConfig condition
# TODO: check HttpRequestMethodConfig condition
# TODO: check SourceIpConfig condition
2021-06-05 09:04:04 +00:00
# TODO: check pattern of value for 'host-header'
# TODO: check pattern of value for 'path-pattern'
2017-08-16 12:09:14 +00:00
# validate Priority
2021-06-05 09:04:04 +00:00
for rule in listener . rules . values ( ) :
2017-08-16 12:09:14 +00:00
if rule . priority == priority :
raise PriorityInUseError ( )
2019-08-09 15:15:56 +00:00
self . _validate_actions ( actions )
2021-06-10 06:02:30 +00:00
arn = listener_arn . replace ( " :listener/ " , " :listener-rule/ " )
arn + = " / %s " % ( get_random_hex ( 16 ) )
2019-08-09 15:15:56 +00:00
# TODO: check for error 'TooManyRegistrationsForTargetId'
# TODO: check for error 'TooManyRules'
# create rule
2021-06-05 09:04:04 +00:00
rule = FakeListenerRule ( listener . arn , arn , conditions , priority , actions , )
listener . register ( arn , rule )
return rule
2019-08-09 15:15:56 +00:00
2021-07-16 07:01:14 +00:00
def _validate_conditions ( self , conditions ) :
for condition in conditions :
if " Field " in condition :
field = condition [ " Field " ]
if field not in [
" host-header " ,
" http-header " ,
" http-request-method " ,
" path-pattern " ,
" query-string " ,
" source-ip " ,
] :
raise InvalidConditionFieldError ( field )
if " Values " in condition and field not in [
" host-header " ,
" path-pattern " ,
] :
raise InvalidConditionValueError (
" The ' Values ' field is not compatible with ' %s ' " % field
)
else :
method_name = " _validate_ " + field . replace ( " - " , " _ " ) + " _condition "
func = getattr ( self , method_name )
func ( condition )
def _validate_host_header_condition ( self , condition ) :
values = None
if " HostHeaderConfig " in condition :
values = condition [ " HostHeaderConfig " ] [ " Values " ]
elif " Values " in condition :
values = condition [ " Values " ]
if len ( values ) > 1 :
raise InvalidConditionValueError (
" The ' host-header ' field contains too many values; the limit is ' 1 ' "
)
if values is None or len ( values ) == 0 :
raise InvalidConditionValueError ( " A condition value must be specified " )
for value in values :
if len ( value ) > 128 :
raise InvalidConditionValueError (
" The ' host-header ' value is too long; the limit is ' 128 ' "
)
def _validate_http_header_condition ( self , condition ) :
if " HttpHeaderConfig " in condition :
config = condition [ " HttpHeaderConfig " ]
name = config . get ( " HttpHeaderName " )
if len ( name ) > 40 :
raise InvalidConditionValueError (
" The ' HttpHeaderName ' value is too long; the limit is ' 40 ' "
)
values = config [ " Values " ]
for value in values :
if len ( value ) > 128 :
raise InvalidConditionValueError (
" The ' http-header ' value is too long; the limit is ' 128 ' "
)
else :
raise InvalidConditionValueError (
" A ' HttpHeaderConfig ' must be specified with ' http-header ' "
)
def _validate_http_request_method_condition ( self , condition ) :
if " HttpRequestMethodConfig " in condition :
for value in condition [ " HttpRequestMethodConfig " ] [ " Values " ] :
if len ( value ) > 40 :
raise InvalidConditionValueError (
" The ' http-request-method ' value is too long; the limit is ' 40 ' "
)
if not re . match ( " [A-Z_-]+ " , value ) :
raise InvalidConditionValueError (
" The ' http-request-method ' value is invalid; the allowed characters are A-Z, hyphen and underscore "
)
else :
raise InvalidConditionValueError (
" A ' HttpRequestMethodConfig ' must be specified with ' http-request-method ' "
)
def _validate_path_pattern_condition ( self , condition ) :
values = None
if " PathPatternConfig " in condition :
values = condition [ " PathPatternConfig " ] [ " Values " ]
elif " Values " in condition :
values = condition [ " Values " ]
if len ( values ) > 1 :
raise InvalidConditionValueError (
" The ' path-pattern ' field contains too many values; the limit is ' 1 ' "
)
if values is None or len ( values ) == 0 :
raise InvalidConditionValueError ( " A condition value must be specified " )
2021-07-26 14:21:17 +00:00
if condition . get ( " Values " ) and condition . get ( " PathPatternConfig " ) :
raise InvalidConditionValueError (
" You cannot provide both Values and ' PathPatternConfig ' for a condition of type ' path-pattern ' "
)
2021-07-16 07:01:14 +00:00
for value in values :
if len ( value ) > 128 :
raise InvalidConditionValueError (
" The ' path-pattern ' value is too long; the limit is ' 128 ' "
)
def _validate_source_ip_condition ( self , condition ) :
if " SourceIpConfig " in condition :
values = condition [ " SourceIpConfig " ] . get ( " Values " , [ ] )
if len ( values ) == 0 :
raise InvalidConditionValueError (
" A ' source-ip ' value must be specified "
)
else :
raise InvalidConditionValueError (
" A ' SourceIpConfig ' must be specified with ' source-ip ' "
)
def _validate_query_string_condition ( self , condition ) :
if " QueryStringConfig " in condition :
config = condition [ " QueryStringConfig " ]
values = config [ " Values " ]
for value in values :
if " Value " not in value :
raise InvalidConditionValueError (
" A ' Value ' must be specified in ' QueryStringKeyValuePair ' "
)
if " Key " in value and len ( value [ " Key " ] ) > 128 :
raise InvalidConditionValueError (
" The ' Key ' value is too long; the limit is ' 128 ' "
)
if len ( value [ " Value " ] ) > 128 :
raise InvalidConditionValueError (
" The ' Value ' value is too long; the limit is ' 128 ' "
)
else :
raise InvalidConditionValueError (
" A ' QueryStringConfig ' must be specified with ' query-string ' "
)
2021-12-20 17:06:57 +00:00
def _get_target_group_arns_from ( self , action_data ) :
if " TargetGroupArn " in action_data :
return [ action_data [ " TargetGroupArn " ] ]
elif " ForwardConfig " in action_data :
return [
tg [ " TargetGroupArn " ]
for tg in action_data [ " ForwardConfig " ] . get ( " TargetGroups " , [ ] )
]
else :
return [ ]
2019-08-09 15:15:56 +00:00
def _validate_actions ( self , actions ) :
2017-08-16 12:09:14 +00:00
# validate Actions
2019-10-31 15:44:26 +00:00
target_group_arns = [
target_group . arn for target_group in self . target_groups . values ( )
]
2017-08-16 12:09:14 +00:00
for i , action in enumerate ( actions ) :
index = i + 1
2019-08-10 04:34:52 +00:00
action_type = action . type
2021-12-20 17:06:57 +00:00
if action_type == " forward " :
found_arns = self . _get_target_group_arns_from ( action_data = action . data )
for target_group_arn in found_arns :
if target_group_arn not in target_group_arns :
raise ActionTargetGroupNotFoundError ( target_group_arn )
2019-10-31 15:44:26 +00:00
elif action_type == " fixed-response " :
2019-09-12 16:29:03 +00:00
self . _validate_fixed_response_action ( action , i , index )
2019-10-31 15:44:26 +00:00
elif action_type in [ " redirect " , " authenticate-cognito " ] :
2019-05-25 10:18:39 +00:00
pass
2021-06-09 17:41:18 +00:00
# pass if listener rule has forward_config as an Action property
2021-10-11 21:12:38 +00:00
elif action_type == " forward " and " ForwardConfig " in action . data . keys ( ) :
2021-06-09 17:41:18 +00:00
pass
2019-05-25 10:18:39 +00:00
else :
2017-08-16 12:09:14 +00:00
raise InvalidActionTypeError ( action_type , index )
2019-09-12 16:29:03 +00:00
def _validate_fixed_response_action ( self , action , i , index ) :
2021-10-11 21:12:38 +00:00
status_code = action . data . get ( " FixedResponseConfig " , { } ) . get ( " StatusCode " )
2019-09-12 16:29:03 +00:00
if status_code is None :
raise ParamValidationError (
2019-10-31 15:44:26 +00:00
report = ' Missing required parameter in Actions[ %s ].FixedResponseConfig: " StatusCode " '
% i
)
2020-03-25 18:07:59 +00:00
expression = r " ^(2|4|5) \ d \ d$ "
if not re . match ( expression , status_code ) :
2019-09-12 16:29:03 +00:00
raise InvalidStatusCodeActionTypeError (
2020-03-25 18:07:59 +00:00
" 1 validation error detected: Value ' {} ' at ' actions. {} .member.fixedResponseConfig.statusCode ' failed to satisfy constraint: \
Member must satisfy regular expression pattern : { } " .format(
status_code , index , expression
)
2019-09-12 16:29:03 +00:00
)
2021-10-11 21:12:38 +00:00
content_type = action . data [ " FixedResponseConfig " ] . get ( " ContentType " )
2019-10-31 15:44:26 +00:00
if content_type and content_type not in [
" text/plain " ,
" text/css " ,
" text/html " ,
" application/javascript " ,
" application/json " ,
] :
2019-09-12 16:29:03 +00:00
raise InvalidLoadBalancerActionException (
" The ContentType must be one of: ' text/html ' , ' application/json ' , ' application/javascript ' , ' text/css ' , ' text/plain ' "
)
2017-07-20 22:00:30 +00:00
def create_target_group ( self , name , * * kwargs ) :
2017-08-18 14:54:28 +00:00
if len ( name ) > 32 :
2017-08-18 15:16:11 +00:00
raise InvalidTargetGroupNameError (
2020-03-25 18:07:59 +00:00
" Target group name ' {} ' cannot be longer than ' 32 ' characters " . format (
name
)
2017-08-18 15:16:11 +00:00
)
2020-03-25 18:07:59 +00:00
if not re . match ( r " ^[a-zA-Z0-9 \ -]+$ " , name ) :
2017-08-18 15:16:11 +00:00
raise InvalidTargetGroupNameError (
2020-03-25 18:07:59 +00:00
" Target group name ' {} ' can only contain characters that are alphanumeric characters or hyphens(-) " . format (
name
)
2017-08-18 15:16:11 +00:00
)
# undocumented validation
2020-03-25 18:07:59 +00:00
if not re . match ( r " (?!.*--)(?!^-)(?!.*-$)^[A-Za-z0-9-]+$ " , name ) :
2017-08-18 15:16:11 +00:00
raise InvalidTargetGroupNameError (
2019-10-31 15:44:26 +00:00
" 1 validation error detected: Value ' %s ' at ' targetGroup.targetGroupArn.targetGroupName ' failed to satisfy constraint: Member must satisfy regular expression pattern: (?!.*--)(?!^-)(?!.*-$)^[A-Za-z0-9-]+$ "
% name
2017-08-18 15:16:11 +00:00
)
2019-10-31 15:44:26 +00:00
if name . startswith ( " - " ) or name . endswith ( " - " ) :
2017-08-18 15:16:11 +00:00
raise InvalidTargetGroupNameError (
" Target group name ' %s ' cannot begin or end with ' - ' " % name
)
2017-07-20 22:00:30 +00:00
for target_group in self . target_groups . values ( ) :
if target_group . name == name :
raise DuplicateTargetGroupName ( )
2019-10-31 15:44:26 +00:00
valid_protocols = [ " HTTPS " , " HTTP " , " TCP " ]
if (
kwargs . get ( " healthcheck_protocol " )
and kwargs [ " healthcheck_protocol " ] not in valid_protocols
) :
2017-10-27 01:16:06 +00:00
raise InvalidConditionValueError (
" Value {} at ' healthCheckProtocol ' failed to satisfy constraint: "
2019-10-31 15:44:26 +00:00
" Member must satisfy enum value set: {} " . format (
kwargs [ " healthcheck_protocol " ] , valid_protocols
)
)
if kwargs . get ( " protocol " ) and kwargs [ " protocol " ] not in valid_protocols :
2017-10-27 01:16:06 +00:00
raise InvalidConditionValueError (
" Value {} at ' protocol ' failed to satisfy constraint: "
2019-10-31 15:44:26 +00:00
" Member must satisfy enum value set: {} " . format (
kwargs [ " protocol " ] , valid_protocols
)
)
2017-10-27 01:16:06 +00:00
2019-10-31 15:44:26 +00:00
if (
kwargs . get ( " matcher " )
and FakeTargetGroup . HTTP_CODE_REGEX . match ( kwargs [ " matcher " ] [ " HttpCode " ] )
is None
) :
raise RESTError (
" InvalidParameterValue " ,
" HttpCode must be like 200 | 200-399 | 200,201 ... " ,
)
2017-10-29 14:14:17 +00:00
2019-10-31 15:44:26 +00:00
arn = make_arn_for_target_group (
account_id = 1 , name = name , region_name = self . region_name
)
2017-07-20 22:00:30 +00:00
target_group = FakeTargetGroup ( name , arn , * * kwargs )
self . target_groups [ target_group . arn ] = target_group
return target_group
2021-07-01 15:25:40 +00:00
def convert_and_validate_certificates ( self , certificates ) :
# transform default certificate to conform with the rest of the code and XML templates
for cert in certificates or [ ] :
cert [ " certificate_arn " ] = cert [ " CertificateArn " ]
return certificates
2021-06-11 20:56:28 +00:00
def convert_and_validate_properties ( self , properties ) :
# transform default actions to confirm with the rest of the code and XML templates
2021-10-11 21:12:38 +00:00
# Caller: CF create/update for type "AWS::ElasticLoadBalancingV2::Listener"
2021-06-11 20:56:28 +00:00
default_actions = [ ]
for i , action in enumerate ( properties [ " DefaultActions " ] ) :
action_type = action [ " Type " ]
if action_type == " forward " :
default_actions . append (
2021-10-11 21:12:38 +00:00
{ " Type " : action_type , " TargetGroupArn " : action [ " TargetGroupArn " ] }
2021-06-11 20:56:28 +00:00
)
elif action_type in [
" redirect " ,
" authenticate-cognito " ,
" fixed-response " ,
] :
2021-10-11 21:12:38 +00:00
default_actions . append ( action )
2021-06-11 20:56:28 +00:00
else :
raise InvalidActionTypeError ( action_type , i + 1 )
return default_actions
2019-10-31 15:44:26 +00:00
def create_listener (
self ,
load_balancer_arn ,
protocol ,
port ,
ssl_policy ,
certificate ,
default_actions ,
) :
2019-08-10 04:34:52 +00:00
default_actions = [ FakeAction ( action ) for action in default_actions ]
2017-07-20 22:00:30 +00:00
balancer = self . load_balancers . get ( load_balancer_arn )
if balancer is None :
raise LoadBalancerNotFoundError ( )
if port in balancer . listeners :
raise DuplicateListenerError ( )
2019-08-10 04:34:52 +00:00
self . _validate_actions ( default_actions )
2019-10-31 15:44:26 +00:00
arn = load_balancer_arn . replace ( " :loadbalancer/ " , " :listener/ " ) + " / %s %s " % (
port ,
id ( self ) ,
)
listener = FakeListener (
load_balancer_arn ,
arn ,
protocol ,
port ,
ssl_policy ,
certificate ,
default_actions ,
)
2017-07-20 22:00:30 +00:00
balancer . listeners [ listener . arn ] = listener
2019-08-10 04:34:52 +00:00
for action in default_actions :
2019-10-31 15:44:26 +00:00
if action . type == " forward " :
2021-12-20 17:06:57 +00:00
found_arns = self . _get_target_group_arns_from ( action_data = action . data )
for arn in found_arns :
target_group = self . target_groups [ arn ]
target_group . load_balancer_arns . append ( load_balancer_arn )
2019-05-25 10:18:39 +00:00
2017-07-20 22:00:30 +00:00
return listener
def describe_load_balancers ( self , arns , names ) :
balancers = self . load_balancers . values ( )
arns = arns or [ ]
names = names or [ ]
if not arns and not names :
2021-05-18 06:52:39 +00:00
for balancer in balancers :
balancer . activate ( )
2017-07-20 22:00:30 +00:00
return balancers
matched_balancers = [ ]
matched_balancer = None
for arn in arns :
for balancer in balancers :
2021-05-18 06:52:39 +00:00
balancer . activate ( )
2017-07-20 22:00:30 +00:00
if balancer . arn == arn :
matched_balancer = balancer
if matched_balancer is None :
raise LoadBalancerNotFoundError ( )
elif matched_balancer not in matched_balancers :
matched_balancers . append ( matched_balancer )
for name in names :
for balancer in balancers :
2021-05-18 06:52:39 +00:00
balancer . activate ( )
2017-07-20 22:00:30 +00:00
if balancer . name == name :
matched_balancer = balancer
if matched_balancer is None :
raise LoadBalancerNotFoundError ( )
elif matched_balancer not in matched_balancers :
matched_balancers . append ( matched_balancer )
return matched_balancers
2017-08-16 16:57:02 +00:00
def describe_rules ( self , listener_arn , rule_arns ) :
if listener_arn is None and not rule_arns :
raise InvalidDescribeRulesRequest (
" You must specify either listener rule ARNs or a listener ARN "
)
if listener_arn is not None and rule_arns is not None :
raise InvalidDescribeRulesRequest (
2019-10-31 15:44:26 +00:00
" Listener rule ARNs and a listener ARN cannot be specified at the same time "
2017-08-16 16:57:02 +00:00
)
if listener_arn :
listener = self . describe_listeners ( None , [ listener_arn ] ) [ 0 ]
2021-06-05 09:04:04 +00:00
return listener . rules . values ( )
2017-08-16 16:57:02 +00:00
# search for rule arns
matched_rules = [ ]
for load_balancer_arn in self . load_balancers :
listeners = self . load_balancers . get ( load_balancer_arn ) . listeners . values ( )
for listener in listeners :
2021-06-05 09:04:04 +00:00
for rule in listener . rules . values ( ) :
2017-08-16 16:57:02 +00:00
if rule . arn in rule_arns :
matched_rules . append ( rule )
return matched_rules
2017-07-20 22:00:30 +00:00
def describe_target_groups ( self , load_balancer_arn , target_group_arns , names ) :
if load_balancer_arn :
if load_balancer_arn not in self . load_balancers :
raise LoadBalancerNotFoundError ( )
2019-10-31 15:44:26 +00:00
return [
tg
for tg in self . target_groups . values ( )
if load_balancer_arn in tg . load_balancer_arns
]
2017-07-20 22:00:30 +00:00
if target_group_arns :
try :
return [ self . target_groups [ arn ] for arn in target_group_arns ]
except KeyError :
raise TargetGroupNotFoundError ( )
if names :
matched = [ ]
for name in names :
found = None
2017-08-18 06:56:53 +00:00
for target_group in self . target_groups . values ( ) :
2017-07-20 22:00:30 +00:00
if target_group . name == name :
found = target_group
if not found :
raise TargetGroupNotFoundError ( )
matched . append ( found )
return matched
return self . target_groups . values ( )
def describe_listeners ( self , load_balancer_arn , listener_arns ) :
if load_balancer_arn :
if load_balancer_arn not in self . load_balancers :
raise LoadBalancerNotFoundError ( )
return self . load_balancers . get ( load_balancer_arn ) . listeners . values ( )
matched = [ ]
for load_balancer in self . load_balancers . values ( ) :
for listener_arn in listener_arns :
listener = load_balancer . listeners . get ( listener_arn )
2021-02-02 15:21:16 +00:00
if listener :
matched . append ( listener )
if listener_arns and len ( matched ) == 0 :
raise ListenerNotFoundError ( )
2017-07-20 22:00:30 +00:00
return matched
def delete_load_balancer ( self , arn ) :
self . load_balancers . pop ( arn , None )
2017-08-16 15:35:45 +00:00
def delete_rule ( self , arn ) :
for load_balancer_arn in self . load_balancers :
listeners = self . load_balancers . get ( load_balancer_arn ) . listeners . values ( )
for listener in listeners :
2021-06-05 09:04:04 +00:00
for rule in listener . rules . values ( ) :
2017-08-16 15:35:45 +00:00
if rule . arn == arn :
2021-06-05 09:04:04 +00:00
listener . remove_rule ( rule . arn )
2017-08-16 15:35:45 +00:00
return
# should raise RuleNotFound Error according to the AWS API doc
# however, boto3 does't raise error even if rule is not found
2017-07-20 22:00:30 +00:00
def delete_target_group ( self , target_group_arn ) :
2017-10-02 19:35:52 +00:00
if target_group_arn not in self . target_groups :
raise TargetGroupNotFoundError ( )
target_group = self . target_groups [ target_group_arn ]
2017-07-20 22:00:30 +00:00
if target_group :
2017-10-02 19:35:52 +00:00
if self . _any_listener_using ( target_group_arn ) :
raise ResourceInUseError (
" The target group ' {} ' is currently in use by a listener or a rule " . format (
2019-10-31 15:44:26 +00:00
target_group_arn
)
)
2017-10-02 19:35:52 +00:00
del self . target_groups [ target_group_arn ]
2017-07-20 22:00:30 +00:00
return target_group
def delete_listener ( self , listener_arn ) :
for load_balancer in self . load_balancers . values ( ) :
2017-08-23 06:58:32 +00:00
listener = load_balancer . listeners . pop ( listener_arn , None )
2017-07-20 22:00:30 +00:00
if listener :
return listener
raise ListenerNotFoundError ( )
2017-08-16 17:25:39 +00:00
def modify_rule ( self , rule_arn , conditions , actions ) :
2019-08-10 04:34:52 +00:00
actions = [ FakeAction ( action ) for action in actions ]
2017-08-21 19:28:58 +00:00
# if conditions or actions is empty list, do not update the attributes
if not conditions and not actions :
raise InvalidModifyRuleArgumentsError ( )
2017-08-16 17:25:39 +00:00
rules = self . describe_rules ( listener_arn = None , rule_arns = [ rule_arn ] )
if not rules :
raise RuleNotFoundError ( )
rule = rules [ 0 ]
2021-07-16 07:01:14 +00:00
self . _validate_conditions ( conditions )
# TODO: check pattern of value for 'host-header'
# TODO: check pattern of value for 'path-pattern'
2017-08-16 17:25:39 +00:00
# validate Actions
2019-08-09 15:15:56 +00:00
self . _validate_actions ( actions )
2017-08-16 17:25:39 +00:00
# TODO: check for error 'TooManyRegistrationsForTargetId'
# TODO: check for error 'TooManyRules'
# modify rule
2017-08-21 19:28:58 +00:00
if conditions :
rule . conditions = conditions
if actions :
rule . actions = actions
2021-06-05 09:04:04 +00:00
return rule
2017-08-16 17:25:39 +00:00
2017-07-20 22:00:30 +00:00
def register_targets ( self , target_group_arn , instances ) :
target_group = self . target_groups . get ( target_group_arn )
if target_group is None :
raise TargetGroupNotFoundError ( )
target_group . register ( instances )
def deregister_targets ( self , target_group_arn , instances ) :
target_group = self . target_groups . get ( target_group_arn )
if target_group is None :
raise TargetGroupNotFoundError ( )
target_group . deregister ( instances )
def describe_target_health ( self , target_group_arn , targets ) :
target_group = self . target_groups . get ( target_group_arn )
if target_group is None :
raise TargetGroupNotFoundError ( )
if not targets :
targets = target_group . targets . values ( )
2019-07-18 14:57:27 +00:00
return [ target_group . health_for ( target , self . ec2_backend ) for target in targets ]
2017-07-20 22:00:30 +00:00
2017-08-16 18:10:26 +00:00
def set_rule_priorities ( self , rule_priorities ) :
# validate
2019-10-31 15:44:26 +00:00
priorities = [ rule_priority [ " priority " ] for rule_priority in rule_priorities ]
2017-08-16 18:10:26 +00:00
for priority in set ( priorities ) :
if priorities . count ( priority ) > 1 :
raise DuplicatePriorityError ( priority )
# validate
for rule_priority in rule_priorities :
2019-10-31 15:44:26 +00:00
given_rule_arn = rule_priority [ " rule_arn " ]
priority = rule_priority [ " priority " ]
_given_rules = self . describe_rules (
listener_arn = None , rule_arns = [ given_rule_arn ]
)
2017-08-16 18:10:26 +00:00
if not _given_rules :
raise RuleNotFoundError ( )
given_rule = _given_rules [ 0 ]
listeners = self . describe_listeners ( None , [ given_rule . listener_arn ] )
listener = listeners [ 0 ]
2021-06-05 09:04:04 +00:00
for rule_in_listener in listener . rules . values ( ) :
2017-08-16 18:10:26 +00:00
if rule_in_listener . priority == priority :
raise PriorityInUseError ( )
# modify
modified_rules = [ ]
for rule_priority in rule_priorities :
2019-10-31 15:44:26 +00:00
given_rule_arn = rule_priority [ " rule_arn " ]
priority = rule_priority [ " priority " ]
_given_rules = self . describe_rules (
listener_arn = None , rule_arns = [ given_rule_arn ]
)
2017-08-16 18:10:26 +00:00
if not _given_rules :
raise RuleNotFoundError ( )
given_rule = _given_rules [ 0 ]
given_rule . priority = priority
modified_rules . append ( given_rule )
return modified_rules
2017-10-29 14:14:17 +00:00
def set_ip_address_type ( self , arn , ip_type ) :
2019-10-31 15:44:26 +00:00
if ip_type not in ( " internal " , " dualstack " ) :
raise RESTError (
" InvalidParameterValue " ,
" IpAddressType must be either internal | dualstack " ,
)
2017-10-29 14:14:17 +00:00
balancer = self . load_balancers . get ( arn )
if balancer is None :
raise LoadBalancerNotFoundError ( )
2019-10-31 15:44:26 +00:00
if ip_type == " dualstack " and balancer . scheme == " internal " :
raise RESTError (
" InvalidConfigurationRequest " ,
" Internal load balancers cannot be dualstack " ,
)
2017-10-29 14:14:17 +00:00
balancer . stack = ip_type
def set_security_groups ( self , arn , sec_groups ) :
balancer = self . load_balancers . get ( arn )
if balancer is None :
raise LoadBalancerNotFoundError ( )
# Check all security groups exist
for sec_group_id in sec_groups :
if self . ec2_backend . get_security_group_from_id ( sec_group_id ) is None :
2019-10-31 15:44:26 +00:00
raise RESTError (
" InvalidSecurityGroup " ,
" Security group {0} does not exist " . format ( sec_group_id ) ,
)
2017-10-29 14:14:17 +00:00
balancer . security_groups = sec_groups
def set_subnets ( self , arn , subnets ) :
balancer = self . load_balancers . get ( arn )
if balancer is None :
raise LoadBalancerNotFoundError ( )
subnet_objects = [ ]
sub_zone_list = { }
for subnet in subnets :
try :
subnet = self . ec2_backend . get_subnet ( subnet )
if subnet . availability_zone in sub_zone_list :
2019-10-31 15:44:26 +00:00
raise RESTError (
" InvalidConfigurationRequest " ,
" More than 1 subnet cannot be specified for 1 availability zone " ,
)
2017-10-29 14:14:17 +00:00
sub_zone_list [ subnet . availability_zone ] = subnet . id
subnet_objects . append ( subnet )
except Exception :
raise SubnetNotFoundError ( )
if len ( sub_zone_list ) < 2 :
2019-10-31 15:44:26 +00:00
raise RESTError (
" InvalidConfigurationRequest " ,
" More than 1 availability zone must be specified " ,
)
2017-10-29 14:14:17 +00:00
balancer . subnets = subnet_objects
return sub_zone_list . items ( )
def modify_load_balancer_attributes ( self , arn , attrs ) :
balancer = self . load_balancers . get ( arn )
if balancer is None :
raise LoadBalancerNotFoundError ( )
for key in attrs :
if key not in FakeLoadBalancer . VALID_ATTRS :
2019-10-31 15:44:26 +00:00
raise RESTError (
" InvalidConfigurationRequest " , " Key {0} not valid " . format ( key )
)
2017-10-29 14:14:17 +00:00
balancer . attrs . update ( attrs )
return balancer . attrs
def describe_load_balancer_attributes ( self , arn ) :
balancer = self . load_balancers . get ( arn )
if balancer is None :
raise LoadBalancerNotFoundError ( )
return balancer . attrs
2019-10-31 15:44:26 +00:00
def modify_target_group (
self ,
arn ,
health_check_proto = None ,
health_check_port = None ,
health_check_path = None ,
health_check_interval = None ,
health_check_timeout = None ,
healthy_threshold_count = None ,
unhealthy_threshold_count = None ,
http_codes = None ,
2021-07-26 14:21:17 +00:00
health_check_enabled = None ,
2019-10-31 15:44:26 +00:00
) :
2017-10-29 14:14:17 +00:00
target_group = self . target_groups . get ( arn )
if target_group is None :
raise TargetGroupNotFoundError ( )
2019-10-31 15:44:26 +00:00
if (
http_codes is not None
and FakeTargetGroup . HTTP_CODE_REGEX . match ( http_codes ) is None
) :
raise RESTError (
" InvalidParameterValue " ,
" HttpCode must be like 200 | 200-399 | 200,201 ... " ,
)
2017-10-29 14:14:17 +00:00
if http_codes is not None :
2019-10-31 15:44:26 +00:00
target_group . matcher [ " HttpCode " ] = http_codes
2017-10-29 14:14:17 +00:00
if health_check_interval is not None :
target_group . healthcheck_interval_seconds = health_check_interval
if health_check_path is not None :
target_group . healthcheck_path = health_check_path
if health_check_port is not None :
target_group . healthcheck_port = health_check_port
if health_check_proto is not None :
target_group . healthcheck_protocol = health_check_proto
if health_check_timeout is not None :
target_group . healthcheck_timeout_seconds = health_check_timeout
2021-07-26 14:21:17 +00:00
if health_check_enabled is not None :
target_group . healthcheck_enabled = health_check_enabled
2017-10-29 14:14:17 +00:00
if healthy_threshold_count is not None :
target_group . healthy_threshold_count = healthy_threshold_count
if unhealthy_threshold_count is not None :
target_group . unhealthy_threshold_count = unhealthy_threshold_count
return target_group
2019-10-31 15:44:26 +00:00
def modify_listener (
self ,
arn ,
port = None ,
protocol = None ,
ssl_policy = None ,
certificates = None ,
default_actions = None ,
) :
2019-08-10 04:34:52 +00:00
default_actions = [ FakeAction ( action ) for action in default_actions ]
2017-10-29 14:14:17 +00:00
for load_balancer in self . load_balancers . values ( ) :
if arn in load_balancer . listeners :
break
else :
raise ListenerNotFoundError ( )
listener = load_balancer . listeners [ arn ]
if port is not None :
for listener_arn , current_listener in load_balancer . listeners . items ( ) :
if listener_arn == arn :
continue
listener . port = port
if protocol is not None :
2019-10-31 15:44:26 +00:00
if protocol not in ( " HTTP " , " HTTPS " , " TCP " ) :
raise RESTError (
" UnsupportedProtocol " ,
" Protocol {0} is not supported " . format ( protocol ) ,
)
2017-10-29 14:14:17 +00:00
# HTTPS checks
2019-10-31 15:44:26 +00:00
if protocol == " HTTPS " :
2017-10-29 14:14:17 +00:00
# Check certificates exist
2021-07-01 15:25:40 +00:00
if certificates :
default_cert = certificates [ 0 ]
default_cert_arn = default_cert [ " certificate_arn " ]
try :
self . acm_backend . get_certificate ( default_cert_arn )
except Exception :
2019-10-31 15:44:26 +00:00
raise RESTError (
2021-07-01 15:25:40 +00:00
" CertificateNotFound " ,
" Certificate {0} not found " . format ( default_cert_arn ) ,
2019-10-31 15:44:26 +00:00
)
2021-07-01 15:25:40 +00:00
listener . certificate = default_cert_arn
listener . certificates = certificates
else :
raise RESTError (
" CertificateWereNotPassed " ,
" You must provide a list containing exactly one certificate if the listener protocol is HTTPS. " ,
)
2017-10-29 14:14:17 +00:00
listener . protocol = protocol
if ssl_policy is not None :
# Its already validated in responses.py
listener . ssl_policy = ssl_policy
2019-05-25 10:18:39 +00:00
if default_actions is not None and default_actions != [ ] :
2017-10-29 14:14:17 +00:00
# Is currently not validated
listener . default_actions = default_actions
2021-11-17 21:36:20 +00:00
listener . _default_rule [ 0 ] . actions = default_actions
2017-10-29 14:14:17 +00:00
return listener
2017-10-02 19:35:52 +00:00
def _any_listener_using ( self , target_group_arn ) :
for load_balancer in self . load_balancers . values ( ) :
for listener in load_balancer . listeners . values ( ) :
2021-06-05 09:04:04 +00:00
for rule in listener . rules . values ( ) :
2017-10-02 19:35:52 +00:00
for action in rule . actions :
2021-12-20 17:06:57 +00:00
found_arns = self . _get_target_group_arns_from (
action_data = action . data
)
if target_group_arn in found_arns :
2017-10-02 19:35:52 +00:00
return True
return False
2019-08-30 16:21:11 +00:00
def notify_terminate_instances ( self , instance_ids ) :
for target_group in self . target_groups . values ( ) :
target_group . deregister_terminated_instances ( instance_ids )
2017-07-20 22:00:30 +00:00
elbv2_backends = { }
for region in ec2_backends . keys ( ) :
elbv2_backends [ region ] = ELBv2Backend ( region )