diff --git a/moto/elbv2/exceptions.py b/moto/elbv2/exceptions.py index 569fa7ee..c6e2256e 100644 --- a/moto/elbv2/exceptions.py +++ b/moto/elbv2/exceptions.py @@ -166,3 +166,11 @@ class DuplicatePriorityError(ELBClientError): super(DuplicatePriorityError, self).__init__( "ValidationError", "Priority '%s' was provided multiple times" % invalid_value) + + +class InvalidTargetGroupNameError(ELBClientError): + + def __init__(self, msg): + super(InvalidTargetGroupNameError, self).__init__( + "ValidationError", msg + ) diff --git a/moto/elbv2/models.py b/moto/elbv2/models.py index 87e6d773..0f3ee451 100644 --- a/moto/elbv2/models.py +++ b/moto/elbv2/models.py @@ -1,6 +1,7 @@ from __future__ import unicode_literals import datetime +import re from moto.compat import OrderedDict from moto.core import BaseBackend, BaseModel from moto.ec2.models import ec2_backends @@ -21,7 +22,8 @@ from .exceptions import ( ActionTargetGroupNotFoundError, InvalidDescribeRulesRequest, RuleNotFoundError, - DuplicatePriorityError + DuplicatePriorityError, + InvalidTargetGroupNameError ) @@ -264,6 +266,25 @@ class ELBv2Backend(BaseBackend): return [rule] def create_target_group(self, name, **kwargs): + if len(name) > 32: + raise InvalidTargetGroupNameError( + "Target group name '%s' cannot be longer than '32' characters" % name + ) + if not re.match('^[a-zA-Z0-9\-]+$', name): + raise InvalidTargetGroupNameError( + "Target group name '%s' can only contain characters that are alphanumeric characters or hyphens(-)" % name + ) + + # undocumented validation + if not re.match('(?!.*--)(?!^-)(?!.*-$)^[A-Za-z0-9-]+$', name): + raise InvalidTargetGroupNameError( + "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 + ) + + if name.startswith('-') or name.endswith('-'): + raise InvalidTargetGroupNameError( + "Target group name '%s' cannot begin or end with '-'" % name + ) for target_group in self.target_groups.values(): if target_group.name == name: raise DuplicateTargetGroupName() diff --git a/tests/test_elbv2/test_elbv2.py b/tests/test_elbv2/test_elbv2.py index 4a47397e..43d72b39 100644 --- a/tests/test_elbv2/test_elbv2.py +++ b/tests/test_elbv2/test_elbv2.py @@ -328,6 +328,65 @@ def test_create_target_group_and_listeners(): response.get('TargetGroups').should.have.length_of(0) +@mock_elbv2 +@mock_ec2 +def test_create_invalid_target_group(): + conn = boto3.client('elbv2', region_name='us-east-1') + ec2 = boto3.resource('ec2', region_name='us-east-1') + + vpc = ec2.create_vpc(CidrBlock='172.28.7.0/24', InstanceTenancy='default') + + # Fail to create target group with name which length is 33 + long_name = 'A' * 33 + with assert_raises(ClientError): + conn.create_target_group( + Name=long_name, + Protocol='HTTP', + Port=8080, + VpcId=vpc.id, + HealthCheckProtocol='HTTP', + HealthCheckPort='8080', + HealthCheckPath='/', + HealthCheckIntervalSeconds=5, + HealthCheckTimeoutSeconds=5, + HealthyThresholdCount=5, + UnhealthyThresholdCount=2, + Matcher={'HttpCode': '200'}) + + invalid_names = ['-name', 'name-', '-name-', 'example.com', 'test@test', 'Na--me'] + for name in invalid_names: + with assert_raises(ClientError): + conn.create_target_group( + Name=name, + Protocol='HTTP', + Port=8080, + VpcId=vpc.id, + HealthCheckProtocol='HTTP', + HealthCheckPort='8080', + HealthCheckPath='/', + HealthCheckIntervalSeconds=5, + HealthCheckTimeoutSeconds=5, + HealthyThresholdCount=5, + UnhealthyThresholdCount=2, + Matcher={'HttpCode': '200'}) + + valid_names = ['name', 'Name', '000'] + for name in valid_names: + conn.create_target_group( + Name=name, + Protocol='HTTP', + Port=8080, + VpcId=vpc.id, + HealthCheckProtocol='HTTP', + HealthCheckPort='8080', + HealthCheckPath='/', + HealthCheckIntervalSeconds=5, + HealthCheckTimeoutSeconds=5, + HealthyThresholdCount=5, + UnhealthyThresholdCount=2, + Matcher={'HttpCode': '200'}) + + @mock_elbv2 @mock_ec2 def test_describe_paginated_balancers():