ansible-collections / amazon.aws

Ansible Collection for Amazon AWS
GNU General Public License v3.0
309 stars 340 forks source link

elb_application_lb - Module fails if listener default action is anything other than Forward #2376

Open ichekaldin opened 3 weeks ago

ichekaldin commented 3 weeks ago

Summary

elb_application_lb module fails if any of load balancer listeners contain any action other than forward under DefaultActions.

For example:

- amazon.aws.elb_application_lb:
    name: example
    listeners:
      - Protocol: HTTP
        Port: 80
        DefaultActions:
          - Type: forward
            TargetGroupArn: "{{ target_group_arn }}"
    scheme: internet-facing
    security_groups: "{{ security_groups }}"
    subnets: "{{ subnets }}"

works, while:

- amazon.aws.elb_application_lb:
    name: example
    listeners:
      - Protocol: HTTP
        Port: 80
        DefaultActions:
          - Type: redirect
            RedirectConfig:
              Host: "#{host}"
              Query: "#{query}"
              Path: "/#{path}"
              Port: "443"
              Protocol: HTTPS
              StatusCode: HTTP_301
    scheme: internet-facing
    security_groups: "{{ security_groups }}"
    subnets: "{{ subnets }}"

doesn't.

Same is true of other types of actions: redirect, fixed-response, or authenticate-oidc.

I believe this was introduced in #2050 and is triggered by these lines in:

line 163:

    return sorted(actions, key=lambda x: (x["TargetGroupArn"], x["Type"]))

line 799:

                [{"TargetGroupArn": x["TargetGroupArn"], "Type": x["Type"]} for x in current_default_actions]

Issue Type

Bug Report

Component Name

elb_application_lb

Ansible Version

$ ansible --version
ansible [core 2.18.0]
  config file = /root/.ansible.cfg
  configured module search path = ['/root/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
  ansible python module location = /root/.env/lib/python3.12/site-packages/ansible
  ansible collection location = /root/.ansible/collections:
  executable location = /root/.env/bin/ansible
  python version = 3.12.7 (main, Oct  1 2024, 02:05:46) [Clang 16.0.0 (clang-1600.0.26.3)] (/root/.env/bin/python3.12)
  jinja version = 3.1.4
  libyaml = True

Collection Versions

$ ansible-galaxy collection list
Collection            Version
--------------------- -------
amazon.aws            9.0.0  
ansible.netcommon     7.1.0  
ansible.posix         1.6.0  
ansible.utils         5.1.1  
community.aws         9.0.0
community.general     10.0.0 
community.network     5.0.3  

AWS SDK versions

$ pip show boto boto3 botocore
Name: boto3
Version: 1.35.54
Summary: The AWS SDK for Python
Home-page: https://github.com/boto/boto3
Author: Amazon Web Services
Author-email: 
License: Apache License 2.0
Location: /root/.env/lib/python3.12/site-packages
Requires: botocore, jmespath, s3transfer
Required-by: 
---
Name: botocore
Version: 1.35.54
Summary: Low-level, data-driven core of boto 3.
Home-page: https://github.com/boto/botocore
Author: Amazon Web Services
Author-email: 
License: Apache License 2.0
Location: /root/.env/lib/python3.12/site-packages
Requires: jmespath, python-dateutil, urllib3
Required-by: boto3, s3transfer

Configuration

$ ansible-config dump --only-changed

OS / Environment

No response

Steps to Reproduce

- amazon.aws.elb_application_lb:
    name: example
    listeners:
      - Protocol: HTTP
        Port: 80
        DefaultActions:
          - Type: redirect
            RedirectConfig:
              Host: "#{host}"
              Query: "#{query}"
              Path: "/#{path}"
              Port: "443"
              Protocol: HTTPS
              StatusCode: HTTP_301
      - Protocol: HTTPS
        Port: 443
        Certificates:
          - CertificateArn: "{{ certificate_arn }}"
        DefaultActions:
          - Type: fixed-response
            FixedResponseConfig:
              ContentType: text/plain
              MessageBody: Not available
              StatusCode: "404"
    scheme: internet-facing
    security_groups: "{{ security_groups }}"
    subnets: "{{ subnets }}"

Expected Results

A load balancer is created or updated successfully.

Resulting load balancer has a listener on port 80 that redirects to port 443, and a listener on port 443 that returns a fixed response of HTTP 404.

Actual Results

fatal: [localhost]: FAILED! => {"changed": false, "module_stderr": "Traceback (most recent call last):\n  File \"/root/.ansible/tmp/ansible-tmp-1730787347.3631353-16953-25684946836848/AnsiballZ_elb_application_lb.py\", line 107, in <module>\n    _ansiballz_main()\n  File \"/root/.ansible/tmp/ansible-tmp-1730787347.3631353-16953-25684946836848/AnsiballZ_elb_application_lb.py\", line 99, in _ansiballz_main\n    invoke_module(zipped_mod, temp_path, ANSIBALLZ_PARAMS)\n  File \"/root/.ansible/tmp/ansible-tmp-1730787347.3631353-16953-25684946836848/AnsiballZ_elb_application_lb.py\", line 47, in invoke_module\n    runpy.run_module(mod_name='ansible_collections.amazon.aws.plugins.modules.elb_application_lb', init_globals=dict(_module_fqn='ansible_collections.amazon.aws.plugins.modules.elb_application_lb', _modlib_path=modlib_path),\n  File \"<frozen runpy>\", line 226, in run_module\n  File \"<frozen runpy>\", line 98, in _run_module_code\n  File \"<frozen runpy>\", line 88, in _run_code\n  File \"/tmp/ansible_amazon.aws.elb_application_lb_payload_udakew3v/ansible_amazon.aws.elb_application_lb_payload.zip/ansible_collections/amazon/aws/plugins/modules/elb_application_lb.py\", line 1060, in <module>\n  File \"/tmp/ansible_amazon.aws.elb_application_lb_payload_udakew3v/ansible_amazon.aws.elb_application_lb_payload.zip/ansible_collections/amazon/aws/plugins/modules/elb_application_lb.py\", line 1052, in main\n  File \"/tmp/ansible_amazon.aws.elb_application_lb_payload_udakew3v/ansible_amazon.aws.elb_application_lb_payload.zip/ansible_collections/amazon/aws/plugins/modules/elb_application_lb.py\", line 860, in create_or_update_alb\n  File \"/tmp/ansible_amazon.aws.elb_application_lb_payload_udakew3v/ansible_amazon.aws.elb_application_lb_payload.zip/ansible_collections/amazon/aws/plugins/module_utils/elbv2.py\", line 918, in compare_listeners\n  File \"/tmp/ansible_amazon.aws.elb_application_lb_payload_udakew3v/ansible_amazon.aws.elb_application_lb_payload.zip/ansible_collections/amazon/aws/plugins/module_utils/elbv2.py\", line 850, in _group_listeners\n  File \"/tmp/ansible_amazon.aws.elb_application_lb_payload_udakew3v/ansible_amazon.aws.elb_application_lb_payload.zip/ansible_collections/amazon/aws/plugins/module_utils/elbv2.py\", line 799, in _compare_listener\nKeyError: 'TargetGroupArn'\n", "module_stdout": "", "msg": "MODULE FAILURE: No start of json char found\nSee stdout/stderr for the exact error", "rc": 1}

Code of Conduct

sebrhex commented 6 days ago

Same here, is there a workaround like pinning a version?

pspoc-ac-yuri-bucci commented 5 days ago

Same here, i fixed last version 8.2.1 to workaround

fabricat-mdb commented 1 day ago

As a workaround, I'm using _sort_actions instead of _sort_listener_actions in elbv2.py, and I've partially applied the changes proposed in PR https://github.com/ansible-collections/amazon.aws/pull/2377. So my code becomes like this:

            current_actions_sorted = _sort_actions(
                {
                    k: v
                    for k, v in x.items()
                    if k
                    in ["AuthenticateOidcConfig", "FixedResponseConfig", "RedirectConfig", "TargetGroupArn", "Type"]
                }
                for x in current_default_actions
            )
            if current_actions_sorted != _sort_actions(new_default_actions):

_sort_actions simply uses Order as the sorting key. I noticed that Order is always present in current_listener (and is required in module parameters) when the listener has more than one default action. When there is only one default action, sorting should not change lists.

Now, the module does not fail anymore, but it always returns as "changed". This is probably related to Order being defaulted to 0 instead of 1 here, and/or to AuthenticateOidcConfig.ClientSecret being not returned in the AuthenticateOidcActionConfig object.

This is what I could do: I lack some skills to code a 100% working solution, sorry.