diff --git a/moto/elbv2/models.py b/moto/elbv2/models.py index 585e85013..bdfc11fbd 100644 --- a/moto/elbv2/models.py +++ b/moto/elbv2/models.py @@ -8,8 +8,6 @@ from collections import OrderedDict from moto.core.exceptions import RESTError from moto.core import BaseBackend, BaseModel, CloudFormationModel from moto.core.utils import ( - camelcase_to_underscores, - underscores_to_camelcase, iso_8601_datetime_with_milliseconds, get_random_hex, ) @@ -367,42 +365,42 @@ class FakeRule(BaseModel): class FakeAction(BaseModel): def __init__(self, data): self.data = data - self.type = data.get("type") + self.type = data.get("Type") def to_xml(self): template = Template( """{{ action.type }} - {% if action.type == "forward" and "forward_config" in action.data %} + {% if action.type == "forward" and "ForwardConfig" in action.data %} - {% for target_group in action.data["forward_config"]["target_groups"] %} + {% for target_group in action.data["ForwardConfig"]["TargetGroups"] %} - {{ target_group["target_group_arn"] }} - {{ target_group["weight"] }} + {{ target_group["TargetGroupArn"] }} + {{ target_group["Weight"] }} {% endfor %} {% endif %} - {% if action.type == "forward" and "forward_config" not in action.data %} - {{ action.data["target_group_arn"] }} + {% if action.type == "forward" and "ForwardConfig" not in action.data %} + {{ action.data["TargetGroupArn"] }} {% elif action.type == "redirect" %} - {{ action.data["redirect_config._protocol"] }} - {{ action.data["redirect_config._port"] }} - {{ action.data["redirect_config._status_code"] }} + {{ action.data["RedirectConfig"]["Protocol"] }} + {{ action.data["RedirectConfig"]["Port"] }} + {{ action.data["RedirectConfig"]["StatusCode"] }} {% elif action.type == "authenticate-cognito" %} - {{ action.data["authenticate_cognito_config._user_pool_arn"] }} - {{ action.data["authenticate_cognito_config._user_pool_client_id"] }} - {{ action.data["authenticate_cognito_config._user_pool_domain"] }} + {{ action.data["AuthenticateCognitoConfig"]["UserPoolArn"] }} + {{ action.data["AuthenticateCognitoConfig"]["UserPoolClientId"] }} + {{ action.data["AuthenticateCognitoConfig"]["UserPoolDomain"] }} {% elif action.type == "fixed-response" %} - {{ action.data["fixed_response_config._content_type"] }} - {{ action.data["fixed_response_config._message_body"] }} - {{ action.data["fixed_response_config._status_code"] }} + {{ action.data["FixedResponseConfig"]["ContentType"] }} + {{ action.data["FixedResponseConfig"]["MessageBody"] }} + {{ action.data["FixedResponseConfig"]["StatusCode"] }} {% endif %} """ @@ -634,45 +632,13 @@ class ELBv2Backend(BaseBackend): default_actions = [] for i, action in enumerate(properties["Actions"]): action_type = action["Type"] - if action_type == "forward" and "ForwardConfig" in action: - action_forward_config = action["ForwardConfig"] - action_target_groups = action_forward_config["TargetGroups"] - target_group_action = [] - for action_target_group in action_target_groups: - target_group_action.append( - { - "target_group_arn": action_target_group["TargetGroupArn"], - "weight": action_target_group["Weight"], - } - ) - default_actions.append( - { - "type": action_type, - "forward_config": {"target_groups": target_group_action}, - } - ) - elif action_type == "forward" and "ForwardConfig" not in action: - default_actions.append( - {"type": action_type, "target_group_arn": action["TargetGroupArn"]} - ) - elif action_type in [ + if action_type in [ "redirect", "authenticate-cognito", "fixed-response", + "forward", ]: - redirect_action = {"type": action_type} - key = ( - underscores_to_camelcase(action_type.capitalize().replace("-", "_")) - + "Config" - ) - for redirect_config_key, redirect_config_value in action[key].items(): - # need to match the output of _get_list_prefix - redirect_action[ - camelcase_to_underscores(key) - + "._" - + camelcase_to_underscores(redirect_config_key) - ] = redirect_config_value - default_actions.append(redirect_action) + default_actions.append(action) else: raise InvalidActionTypeError(action_type, i + 1) return default_actions @@ -853,8 +819,8 @@ class ELBv2Backend(BaseBackend): for i, action in enumerate(actions): index = i + 1 action_type = action.type - if action_type == "forward" and "target_group_arn" in action.data: - action_target_group_arn = action.data["target_group_arn"] + if action_type == "forward" and "TargetGroupArn" in action.data: + action_target_group_arn = action.data["TargetGroupArn"] if action_target_group_arn not in target_group_arns: raise ActionTargetGroupNotFoundError(action_target_group_arn) elif action_type == "fixed-response": @@ -862,20 +828,13 @@ class ELBv2Backend(BaseBackend): elif action_type in ["redirect", "authenticate-cognito"]: pass # pass if listener rule has forward_config as an Action property - elif ( - action_type == "forward" - and "forward_config._target_groups.member.{}._target_group_arn".format( - index - ) - in action.data.keys() - or "forward_config" in action.data.keys() - ): + elif action_type == "forward" and "ForwardConfig" in action.data.keys(): pass else: raise InvalidActionTypeError(action_type, index) def _validate_fixed_response_action(self, action, i, index): - status_code = action.data.get("fixed_response_config._status_code") + status_code = action.data.get("FixedResponseConfig", {}).get("StatusCode") if status_code is None: raise ParamValidationError( report='Missing required parameter in Actions[%s].FixedResponseConfig: "StatusCode"' @@ -889,7 +848,7 @@ Member must satisfy regular expression pattern: {}".format( status_code, index, expression ) ) - content_type = action.data["fixed_response_config._content_type"] + content_type = action.data["FixedResponseConfig"].get("ContentType") if content_type and content_type not in [ "text/plain", "text/css", @@ -977,31 +936,20 @@ Member must satisfy regular expression pattern: {}".format( def convert_and_validate_properties(self, properties): # transform default actions to confirm with the rest of the code and XML templates + # Caller: CF create/update for type "AWS::ElasticLoadBalancingV2::Listener" default_actions = [] for i, action in enumerate(properties["DefaultActions"]): action_type = action["Type"] if action_type == "forward": default_actions.append( - {"type": action_type, "target_group_arn": action["TargetGroupArn"]} + {"Type": action_type, "TargetGroupArn": action["TargetGroupArn"]} ) elif action_type in [ "redirect", "authenticate-cognito", "fixed-response", ]: - redirect_action = {"type": action_type} - key = ( - underscores_to_camelcase(action_type.capitalize().replace("-", "_")) - + "Config" - ) - for redirect_config_key, redirect_config_value in action[key].items(): - # need to match the output of _get_list_prefix - redirect_action[ - camelcase_to_underscores(key) - + "._" - + camelcase_to_underscores(redirect_config_key) - ] = redirect_config_value - default_actions.append(redirect_action) + default_actions.append(action) else: raise InvalidActionTypeError(action_type, i + 1) return default_actions @@ -1040,7 +988,7 @@ Member must satisfy regular expression pattern: {}".format( balancer.listeners[listener.arn] = listener for action in default_actions: if action.type == "forward": - target_group = self.target_groups[action.data["target_group_arn"]] + target_group = self.target_groups[action.data["TargetGroupArn"]] target_group.load_balancer_arns.append(load_balancer_arn) return listener @@ -1473,7 +1421,7 @@ Member must satisfy regular expression pattern: {}".format( for listener in load_balancer.listeners.values(): for rule in listener.rules.values(): for action in rule.actions: - if action.data.get("target_group_arn") == target_group_arn: + if action.data.get("TargetGroupArn") == target_group_arn: return True return False diff --git a/moto/elbv2/responses.py b/moto/elbv2/responses.py index 9b9d400d6..d62115bf6 100644 --- a/moto/elbv2/responses.py +++ b/moto/elbv2/responses.py @@ -165,12 +165,11 @@ class ELBV2Response(BaseResponse): @amzn_request_id def create_rule(self): params = self._get_params() - actions = self._get_list_prefix("Actions.member") rules = self.elbv2_backend.create_rule( listener_arn=params["ListenerArn"], conditions=params["Conditions"], priority=params["Priority"], - actions=actions, + actions=params["Actions"], ) template = self.response_template(CREATE_RULE_TEMPLATE) return template.render(rules=rules) @@ -214,6 +213,7 @@ class ELBV2Response(BaseResponse): @amzn_request_id def create_listener(self): + params = self._get_params() load_balancer_arn = self._get_param("LoadBalancerArn") protocol = self._get_param("Protocol") port = self._get_param("Port") @@ -223,7 +223,7 @@ class ELBV2Response(BaseResponse): certificate = certificates[0].get("certificate_arn") else: certificate = None - default_actions = self._get_list_prefix("DefaultActions.member") + default_actions = params.get("DefaultActions", []) listener = self.elbv2_backend.create_listener( load_balancer_arn=load_balancer_arn, @@ -357,7 +357,7 @@ class ELBV2Response(BaseResponse): rule_arn = self._get_param("RuleArn") params = self._get_params() conditions = params["Conditions"] - actions = self._get_list_prefix("Actions.member") + actions = params.get("Actions", []) rules = self.elbv2_backend.modify_rule( rule_arn=rule_arn, conditions=conditions, actions=actions ) diff --git a/tests/test_elbv2/test_elbv2.py b/tests/test_elbv2/test_elbv2.py index e25247a76..e88517dbd 100644 --- a/tests/test_elbv2/test_elbv2.py +++ b/tests/test_elbv2/test_elbv2.py @@ -434,6 +434,83 @@ def test_create_target_group_without_non_required_parameters(): target_group.should_not.be.none +@mock_ec2 +@mock_elbv2 +def test_create_rule_forward_config_as_second_arg(): + # https://github.com/spulec/moto/issues/4123 + # Necessary because there was some convoluted way of parsing arguments + # Actions with type=forward had to be the first action specified + response, vpc, security_group, subnet1, subnet2, elbv2 = create_load_balancer() + + load_balancer_arn = response.get("LoadBalancers")[0].get("LoadBalancerArn") + + response = elbv2.create_listener( + LoadBalancerArn=load_balancer_arn, Protocol="HTTP", Port=80, DefaultActions=[], + ) + http_listener_arn = response.get("Listeners")[0]["ListenerArn"] + + priority = 100 + + response = elbv2.create_target_group( + Name="a-target", + Protocol="HTTP", + Port=8080, + VpcId=vpc.id, + HealthCheckProtocol="HTTP", + HealthCheckPort="8080", + HealthCheckPath="/", + Matcher={"HttpCode": "200"}, + ) + target_group = response.get("TargetGroups")[0] + + # No targets registered yet + target_group_arn = target_group.get("TargetGroupArn") + elbv2.create_rule( + ListenerArn=http_listener_arn, + Conditions=[ + {"Field": "path-pattern", "PathPatternConfig": {"Values": [f"/sth*",]},}, + ], + Priority=priority, + Actions=[ + { + "Type": "authenticate-cognito", + "Order": 1, + "AuthenticateCognitoConfig": { + "UserPoolArn": "?1", + "UserPoolClientId": "?2", + "UserPoolDomain": "?2", + "SessionCookieName": "AWSELBAuthSessionCookie", + "Scope": "openid", + "SessionTimeout": 604800, + "OnUnauthenticatedRequest": "authenticate", + }, + }, + { + "Type": "forward", + "Order": 2, + "ForwardConfig": { + "TargetGroups": [ + {"TargetGroupArn": target_group_arn, "Weight": 1}, + ], + "TargetGroupStickinessConfig": {"Enabled": False,}, + }, + }, + ], + ) + all_rules = elbv2.describe_rules(ListenerArn=http_listener_arn)["Rules"] + our_rule = all_rules[0] + actions = our_rule["Actions"] + forward_action = [a for a in actions if "ForwardConfig" in a.keys()][0] + forward_action.should.equal( + { + "ForwardConfig": { + "TargetGroups": [{"TargetGroupArn": target_group_arn, "Weight": 1}] + }, + "Type": "forward", + } + ) + + @mock_elbv2 @mock_ec2 def test_create_invalid_target_group(): diff --git a/tests/test_elbv2/test_elbv2_cloudformation.py b/tests/test_elbv2/test_elbv2_cloudformation.py index cc7ba8246..d2dc21927 100644 --- a/tests/test_elbv2/test_elbv2_cloudformation.py +++ b/tests/test_elbv2/test_elbv2_cloudformation.py @@ -1,5 +1,6 @@ import boto3 import json +import sure # noqa from moto import mock_elbv2, mock_ec2, mock_cloudformation from moto.core import ACCOUNT_ID