Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support passing arbitrary extra keys to fail_json_aws #140

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions changelogs/fragments/140-fail_json_aws_keys.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
minor_changes:
- module_utils.core - Support passing arbitrary extra keys to fail_json_aws, matching capabilities of fail_json.
4 changes: 3 additions & 1 deletion plugins/module_utils/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,7 @@ def resource(self, service):
def region(self, boto3=True):
return get_aws_region(self, boto3)

def fail_json_aws(self, exception, msg=None):
def fail_json_aws(self, exception, msg=None, **kwargs):
"""call fail_json with processed exception

function for converting exceptions thrown by AWS SDK modules,
Expand Down Expand Up @@ -230,6 +230,8 @@ def fail_json_aws(self, exception, msg=None):
**self._gather_versions()
)

failure.update(kwargs)

if response is not None:
failure.update(**camel_dict_to_snake_dict(response))

Expand Down
2 changes: 1 addition & 1 deletion shippable.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ matrix:
- env: T=aws/3.7/2 A_REV=stable-2.9
- env: T=aws/2.7/2 A_REV=stable-2.10
- env: T=aws/3.7/2 A_REV=stable-2.10

- env: T=aws/2.7/3 A_REV=devel
- env: T=aws/3.7/3 A_REV=devel
- env: T=aws/2.7/3 A_REV=stable-2.9
Expand Down
321 changes: 321 additions & 0 deletions tests/unit/module_utils/core/ansible_aws_module/test_fail_json_aws.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,321 @@
# (c) 2020 Red Hat Inc.
#
# This file is part of Ansible
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)

from __future__ import (absolute_import, division, print_function)
__metaclass__ = type

import pytest
import botocore
import boto3
import json

from ansible.module_utils.common.dict_transformations import camel_dict_to_snake_dict
from ansible_collections.amazon.aws.plugins.module_utils.core import AnsibleAWSModule


class TestFailJsonAws(object):
# ========================================================
# Prepare some data for use in our testing
# ========================================================
def setup_method(self):
# Basic information that ClientError needs to spawn off an error
self.EXAMPLE_EXCEPTION_DATA = {
"Error": {
"Code": "InvalidParameterValue",
"Message": "The filter 'exampleFilter' is invalid"
},
"ResponseMetadata": {
"RequestId": "01234567-89ab-cdef-0123-456789abcdef",
"HTTPStatusCode": 400,
"HTTPHeaders": {
"transfer-encoding": "chunked",
"date": "Fri, 13 Nov 2020 00:00:00 GMT",
"connection": "close",
"server": "AmazonEC2"
},
"RetryAttempts": 0
}
}
self.CAMEL_RESPONSE = camel_dict_to_snake_dict(self.EXAMPLE_EXCEPTION_DATA.get("ResponseMetadata"))
self.CAMEL_ERROR = camel_dict_to_snake_dict(self.EXAMPLE_EXCEPTION_DATA.get("Error"))
# ClientError(EXAMPLE_EXCEPTION_DATA, "testCall") will generate this
self.EXAMPLE_MSG = "An error occurred (InvalidParameterValue) when calling the testCall operation: The filter 'exampleFilter' is invalid"
self.DEFAULT_CORE_MSG = "An unspecified error occurred"
self.FAIL_MSG = "I Failed!"

# ========================================================
# Passing fail_json_aws nothing more than a ClientError
# ========================================================
@pytest.mark.parametrize("stdin", [{}], indirect=["stdin"])
def test_fail_client_minimal(self, monkeypatch, stdin, capfd):
monkeypatch.setattr(botocore, "__version__", "1.2.3")
monkeypatch.setattr(boto3, "__version__", "1.2.4")

# Create a minimal module that we can call
module = AnsibleAWSModule(argument_spec=dict())
try:
raise botocore.exceptions.ClientError(self.EXAMPLE_EXCEPTION_DATA, "testCall")
except botocore.exceptions.ClientError as e:
with pytest.raises(SystemExit) as ctx:
module.fail_json_aws(e)
assert ctx.value.code == 1
out, err = capfd.readouterr()
return_val = json.loads(out)

assert return_val.get("msg") == self.EXAMPLE_MSG
assert return_val.get("boto3_version") == "1.2.4"
assert return_val.get("botocore_version") == "1.2.3"
assert return_val.get("exception") is not None
assert return_val.get("failed")
assert return_val.get("response_metadata") == self.CAMEL_RESPONSE
assert return_val.get("error") == self.CAMEL_ERROR

# ========================================================
# Passing fail_json_aws a ClientError and a message
# ========================================================
@pytest.mark.parametrize("stdin", [{}], indirect=["stdin"])
def test_fail_client_msg(self, monkeypatch, stdin, capfd):
monkeypatch.setattr(botocore, "__version__", "1.2.3")
monkeypatch.setattr(boto3, "__version__", "1.2.4")

# Create a minimal module that we can call
module = AnsibleAWSModule(argument_spec=dict())
try:
raise botocore.exceptions.ClientError(self.EXAMPLE_EXCEPTION_DATA, "testCall")
except botocore.exceptions.ClientError as e:
with pytest.raises(SystemExit) as ctx:
module.fail_json_aws(e, msg=self.FAIL_MSG)
assert ctx.value.code == 1
out, err = capfd.readouterr()
return_val = json.loads(out)

assert return_val.get("msg") == self.FAIL_MSG + ": " + self.EXAMPLE_MSG
assert return_val.get("boto3_version") == "1.2.4"
assert return_val.get("botocore_version") == "1.2.3"
assert return_val.get("exception") is not None
assert return_val.get("failed")
assert return_val.get("response_metadata") == self.CAMEL_RESPONSE
assert return_val.get("error") == self.CAMEL_ERROR

# ========================================================
# Passing fail_json_aws a ClientError and a message as a positional argument
# ========================================================
@pytest.mark.parametrize("stdin", [{}], indirect=["stdin"])
def test_fail_client_positional_msg(self, monkeypatch, stdin, capfd):
monkeypatch.setattr(botocore, "__version__", "1.2.3")
monkeypatch.setattr(boto3, "__version__", "1.2.4")

# Create a minimal module that we can call
module = AnsibleAWSModule(argument_spec=dict())
try:
raise botocore.exceptions.ClientError(self.EXAMPLE_EXCEPTION_DATA, "testCall")
except botocore.exceptions.ClientError as e:
with pytest.raises(SystemExit) as ctx:
module.fail_json_aws(e, self.FAIL_MSG)
assert ctx.value.code == 1
out, err = capfd.readouterr()
return_val = json.loads(out)

assert return_val.get("msg") == self.FAIL_MSG + ": " + self.EXAMPLE_MSG
assert return_val.get("boto3_version") == "1.2.4"
assert return_val.get("botocore_version") == "1.2.3"
assert return_val.get("exception") is not None
assert return_val.get("failed")
assert return_val.get("response_metadata") == self.CAMEL_RESPONSE
assert return_val.get("error") == self.CAMEL_ERROR

# ========================================================
# Passing fail_json_aws a ClientError and an arbitrary key
# ========================================================
@pytest.mark.parametrize("stdin", [{}], indirect=["stdin"])
def test_fail_client_key(self, monkeypatch, stdin, capfd):
monkeypatch.setattr(botocore, "__version__", "1.2.3")
monkeypatch.setattr(boto3, "__version__", "1.2.4")

# Create a minimal module that we can call
module = AnsibleAWSModule(argument_spec=dict())
try:
raise botocore.exceptions.ClientError(self.EXAMPLE_EXCEPTION_DATA, "testCall")
except botocore.exceptions.ClientError as e:
with pytest.raises(SystemExit) as ctx:
module.fail_json_aws(e, extra_key="Some Value")
assert ctx.value.code == 1
out, err = capfd.readouterr()
return_val = json.loads(out)

assert return_val.get("msg") == self.EXAMPLE_MSG
assert return_val.get("extra_key") == "Some Value"
assert return_val.get("boto3_version") == "1.2.4"
assert return_val.get("botocore_version") == "1.2.3"
assert return_val.get("exception") is not None
assert return_val.get("failed")
assert return_val.get("response_metadata") == self.CAMEL_RESPONSE
assert return_val.get("error") == self.CAMEL_ERROR

# ========================================================
# Passing fail_json_aws a ClientError, and arbitraty key and a message
# ========================================================
@pytest.mark.parametrize("stdin", [{}], indirect=["stdin"])
def test_fail_client_msg_and_key(self, monkeypatch, stdin, capfd):
monkeypatch.setattr(botocore, "__version__", "1.2.3")
monkeypatch.setattr(boto3, "__version__", "1.2.4")

# Create a minimal module that we can call
module = AnsibleAWSModule(argument_spec=dict())
try:
raise botocore.exceptions.ClientError(self.EXAMPLE_EXCEPTION_DATA, "testCall")
except botocore.exceptions.ClientError as e:
with pytest.raises(SystemExit) as ctx:
module.fail_json_aws(e, extra_key="Some Value", msg=self.FAIL_MSG)
assert ctx.value.code == 1
out, err = capfd.readouterr()
return_val = json.loads(out)

assert return_val.get("msg") == self.FAIL_MSG + ": " + self.EXAMPLE_MSG
assert return_val.get("extra_key") == "Some Value"
assert return_val.get("boto3_version") == "1.2.4"
assert return_val.get("botocore_version") == "1.2.3"
assert return_val.get("exception") is not None
assert return_val.get("failed")
assert return_val.get("response_metadata") == self.CAMEL_RESPONSE
assert return_val.get("error") == self.CAMEL_ERROR

# ========================================================
# Passing fail_json_aws nothing more than a BotoCoreError
# ========================================================
@pytest.mark.parametrize("stdin", [{}], indirect=["stdin"])
def test_fail_botocore_minimal(self, monkeypatch, stdin, capfd):
monkeypatch.setattr(botocore, "__version__", "1.2.3")
monkeypatch.setattr(boto3, "__version__", "1.2.4")

# Create a minimal module that we can call
module = AnsibleAWSModule(argument_spec=dict())
try:
raise botocore.exceptions.BotoCoreError()
except botocore.exceptions.BotoCoreError as e:
with pytest.raises(SystemExit) as ctx:
module.fail_json_aws(e)
assert ctx.value.code == 1
out, err = capfd.readouterr()
return_val = json.loads(out)

assert return_val.get("msg") == self.DEFAULT_CORE_MSG
assert return_val.get("boto3_version") == "1.2.4"
assert return_val.get("botocore_version") == "1.2.3"
assert return_val.get("exception") is not None
assert return_val.get("failed")
assert "response_metadata" not in return_val
assert "error" not in return_val

# ========================================================
# Passing fail_json_aws BotoCoreError and a message
# ========================================================
@pytest.mark.parametrize("stdin", [{}], indirect=["stdin"])
def test_fail_botocore_msg(self, monkeypatch, stdin, capfd):
monkeypatch.setattr(botocore, "__version__", "1.2.3")
monkeypatch.setattr(boto3, "__version__", "1.2.4")

# Create a minimal module that we can call
module = AnsibleAWSModule(argument_spec=dict())
try:
raise botocore.exceptions.BotoCoreError()
except botocore.exceptions.BotoCoreError as e:
with pytest.raises(SystemExit) as ctx:
module.fail_json_aws(e, msg=self.FAIL_MSG)
assert ctx.value.code == 1
out, err = capfd.readouterr()
return_val = json.loads(out)

assert return_val.get("msg") == self.FAIL_MSG + ": " + self.DEFAULT_CORE_MSG
assert return_val.get("boto3_version") == "1.2.4"
assert return_val.get("botocore_version") == "1.2.3"
assert return_val.get("exception") is not None
assert return_val.get("failed")
assert "response_metadata" not in return_val
assert "error" not in return_val

# ========================================================
# Passing fail_json_aws BotoCoreError and a message as a positional
# argument
# ========================================================
@pytest.mark.parametrize("stdin", [{}], indirect=["stdin"])
def test_fail_botocore_positional_msg(self, monkeypatch, stdin, capfd):
monkeypatch.setattr(botocore, "__version__", "1.2.3")
monkeypatch.setattr(boto3, "__version__", "1.2.4")

# Create a minimal module that we can call
module = AnsibleAWSModule(argument_spec=dict())
try:
raise botocore.exceptions.BotoCoreError()
except botocore.exceptions.BotoCoreError as e:
with pytest.raises(SystemExit) as ctx:
module.fail_json_aws(e, self.FAIL_MSG)
assert ctx.value.code == 1
out, err = capfd.readouterr()
return_val = json.loads(out)

assert return_val.get("msg") == self.FAIL_MSG + ": " + self.DEFAULT_CORE_MSG
assert return_val.get("boto3_version") == "1.2.4"
assert return_val.get("botocore_version") == "1.2.3"
assert return_val.get("exception") is not None
assert return_val.get("failed")
assert "response_metadata" not in return_val
assert "error" not in return_val

# ========================================================
# Passing fail_json_aws a BotoCoreError and an arbitrary key
# ========================================================
@pytest.mark.parametrize("stdin", [{}], indirect=["stdin"])
def test_fail_botocore_key(self, monkeypatch, stdin, capfd):
monkeypatch.setattr(botocore, "__version__", "1.2.3")
monkeypatch.setattr(boto3, "__version__", "1.2.4")

# Create a minimal module that we can call
module = AnsibleAWSModule(argument_spec=dict())
try:
raise botocore.exceptions.BotoCoreError()
except botocore.exceptions.BotoCoreError as e:
with pytest.raises(SystemExit) as ctx:
module.fail_json_aws(e, extra_key="Some Value")
assert ctx.value.code == 1
out, err = capfd.readouterr()
return_val = json.loads(out)

assert return_val.get("msg") == self.DEFAULT_CORE_MSG
assert return_val.get("extra_key") == "Some Value"
assert return_val.get("boto3_version") == "1.2.4"
assert return_val.get("botocore_version") == "1.2.3"
assert return_val.get("exception") is not None
assert return_val.get("failed")
assert "response_metadata" not in return_val
assert "error" not in return_val

# ========================================================
# Passing fail_json_aws BotoCoreError, an arbitry key and a message
# ========================================================
@pytest.mark.parametrize("stdin", [{}], indirect=["stdin"])
def test_fail_botocore_msg_and_key(self, monkeypatch, stdin, capfd):
monkeypatch.setattr(botocore, "__version__", "1.2.3")
monkeypatch.setattr(boto3, "__version__", "1.2.4")

# Create a minimal module that we can call
module = AnsibleAWSModule(argument_spec=dict())
try:
raise botocore.exceptions.BotoCoreError()
except botocore.exceptions.BotoCoreError as e:
with pytest.raises(SystemExit) as ctx:
module.fail_json_aws(e, extra_key="Some Value", msg=self.FAIL_MSG)
assert ctx.value.code == 1
out, err = capfd.readouterr()
return_val = json.loads(out)

assert return_val.get("msg") == self.FAIL_MSG + ": " + self.DEFAULT_CORE_MSG
assert return_val.get("extra_key") == "Some Value"
assert return_val.get("boto3_version") == "1.2.4"
assert return_val.get("botocore_version") == "1.2.3"
assert return_val.get("exception") is not None
assert return_val.get("failed")
assert "response_metadata" not in return_val
assert "error" not in return_val