-
Notifications
You must be signed in to change notification settings - Fork 429
Create S3_cors tests and fix check_mode, other bugs #2471
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
Open
Romi1495
wants to merge
8
commits into
ansible-collections:main
Choose a base branch
from
Romi1495:improve-s3_cors-tests-and-fixes
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
8 commits
Select commit
Hold shift + click to select a range
b70c84f
fix(s3_cors): add check_mode support, fix idempotency, and add integr…
Romi1495 882c385
small edits
Romi1495 a9ab0ee
Update changelog fragment with PR #2471
Romi1495 e435ab4
fixing 'tests' issue and changelog breaking
Romi1495 a734d8a
s3_cors: address review feedback - sort key, unit tests, docs, error …
Romi1495 cf0886f
fix(s3_cors): remove type hint syntax incompatible with older Python
Romi1495 3df70ed
s3_cors: address review - use FQCN, fix docs markup, reclassify chang…
Romi1495 b814e32
s3_cors: clean up unit tests — remove redundancy, improve naming
Romi1495 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Some comments aren't visible on the classic Files Changed page.
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,6 @@ | ||
| --- | ||
| minor_changes: | ||
| - s3_cors - Added support for check_mode operation (https://github.com/ansible-collections/community.aws/pull/2471). | ||
| bugfixes: | ||
| - s3_cors - Fixed idempotency issue in ``state=absent`` where the module always reported changes even when no CORS configuration existed (https://github.com/ansible-collections/community.aws/pull/2471). | ||
| - s3_cors - Fixed CORS rule comparison logic by replacing IAM policy comparison with CORS-specific normalization to ensure accurate change detection (https://github.com/ansible-collections/community.aws/pull/2471). | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -23,9 +23,37 @@ | |||||||||
| type: str | ||||||||||
| rules: | ||||||||||
| description: | ||||||||||
| - Cors rules to put on the S3 bucket. | ||||||||||
| - CORS rules to apply to the S3 bucket. | ||||||||||
| type: list | ||||||||||
| elements: dict | ||||||||||
| suboptions: | ||||||||||
| allowed_origins: | ||||||||||
| description: | ||||||||||
| - One or more origins you want customers to be able to access the bucket from. | ||||||||||
| type: list | ||||||||||
| elements: str | ||||||||||
| required: true | ||||||||||
| allowed_methods: | ||||||||||
| description: | ||||||||||
| - HTTP methods that are allowed from the origin specified in I(allowed_origins). | ||||||||||
| type: list | ||||||||||
| elements: str | ||||||||||
| required: true | ||||||||||
| choices: ['GET', 'PUT', 'HEAD', 'POST', 'DELETE'] | ||||||||||
| allowed_headers: | ||||||||||
| description: | ||||||||||
| - Headers that are specified in the C(Access-Control-Request-Headers) header. | ||||||||||
| type: list | ||||||||||
| elements: str | ||||||||||
| expose_headers: | ||||||||||
| description: | ||||||||||
| - One or more headers in the response that you want customers to be able to access from their applications. | ||||||||||
| type: list | ||||||||||
| elements: str | ||||||||||
| max_age_seconds: | ||||||||||
| description: | ||||||||||
| - Time in seconds that the browser should cache the preflight response for the specified resource. | ||||||||||
| type: int | ||||||||||
| state: | ||||||||||
| description: | ||||||||||
| - Create or remove cors on the S3 bucket. | ||||||||||
|
|
@@ -62,6 +90,26 @@ | |||||||||
| - community.aws.s3_cors: | ||||||||||
| name: mys3bucket | ||||||||||
| state: absent | ||||||||||
|
|
||||||||||
| # Create CORS rules with multiple origins and methods | ||||||||||
| - community.aws.s3_cors: | ||||||||||
|
Comment on lines
+94
to
+95
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||
| name: mys3bucket | ||||||||||
| state: present | ||||||||||
| rules: | ||||||||||
| - allowed_origins: | ||||||||||
| - http://www.example.com | ||||||||||
| - http://www.example.org | ||||||||||
| allowed_methods: | ||||||||||
| - GET | ||||||||||
| - PUT | ||||||||||
| - POST | ||||||||||
| allowed_headers: | ||||||||||
| - '*' | ||||||||||
| max_age_seconds: 3600 | ||||||||||
| - allowed_origins: | ||||||||||
| - '*' | ||||||||||
| allowed_methods: | ||||||||||
| - GET | ||||||||||
| """ | ||||||||||
|
|
||||||||||
| RETURN = r""" | ||||||||||
|
|
@@ -71,13 +119,13 @@ | |||||||||
| type: bool | ||||||||||
| sample: true | ||||||||||
| name: | ||||||||||
| description: name of bucket | ||||||||||
| returned: always | ||||||||||
| description: Name of the S3 bucket. | ||||||||||
| returned: when O(state=present) | ||||||||||
| type: str | ||||||||||
| sample: 'bucket-name' | ||||||||||
| rules: | ||||||||||
| description: list of current rules | ||||||||||
| returned: always | ||||||||||
| description: CORS rules applied to the bucket. | ||||||||||
| returned: when O(state=present) | ||||||||||
| type: list | ||||||||||
| sample: [ | ||||||||||
| { | ||||||||||
|
|
@@ -103,27 +151,65 @@ | |||||||||
|
|
||||||||||
| from ansible.module_utils.common.dict_transformations import snake_dict_to_camel_dict | ||||||||||
|
|
||||||||||
| from ansible_collections.amazon.aws.plugins.module_utils.policy import compare_policies | ||||||||||
| from ansible_collections.amazon.aws.plugins.module_utils.botocore import is_boto3_error_code | ||||||||||
|
|
||||||||||
| from ansible_collections.community.aws.plugins.module_utils.modules import AnsibleCommunityAWSModule as AnsibleAWSModule | ||||||||||
|
|
||||||||||
|
|
||||||||||
| def normalize_cors_rules(rules): | ||||||||||
| """Normalize CORS rules for deterministic comparison. | ||||||||||
|
|
||||||||||
| Sorts list values within each rule (e.g., AllowedOrigins, AllowedMethods) and | ||||||||||
| sorts the rules themselves by a stable tuple-based key so that two equivalent | ||||||||||
| rule sets always compare equal regardless of ordering. | ||||||||||
|
|
||||||||||
| Args: | ||||||||||
| rules: A list of CORS rule dicts as returned by boto3 or passed by the user. | ||||||||||
| May be None or empty. | ||||||||||
|
|
||||||||||
| Returns: | ||||||||||
| A new list of normalized rule dicts, sorted deterministically. | ||||||||||
| """ | ||||||||||
| if not rules: | ||||||||||
| return [] | ||||||||||
|
|
||||||||||
| normalized = [] | ||||||||||
| for rule in rules: | ||||||||||
| norm_rule = {} | ||||||||||
| for key, value in rule.items(): | ||||||||||
| if isinstance(value, list): | ||||||||||
| norm_rule[key] = sorted(value) | ||||||||||
| else: | ||||||||||
| norm_rule[key] = value | ||||||||||
| normalized.append(norm_rule) | ||||||||||
|
|
||||||||||
| def sort_key(rule): | ||||||||||
| """Create a hashable, sortable key from a rule dict.""" | ||||||||||
| return tuple(sorted((k, tuple(v) if isinstance(v, list) else v) for k, v in rule.items())) | ||||||||||
|
|
||||||||||
| return sorted(normalized, key=sort_key) | ||||||||||
|
|
||||||||||
|
|
||||||||||
| def create_or_update_bucket_cors(connection, module): | ||||||||||
| """Create or update CORS configuration on an S3 bucket.""" | ||||||||||
| name = module.params.get("name") | ||||||||||
| rules = module.params.get("rules", []) | ||||||||||
| changed = False | ||||||||||
|
|
||||||||||
| try: | ||||||||||
| current_camel_rules = connection.get_bucket_cors(Bucket=name)["CORSRules"] | ||||||||||
| except ClientError: | ||||||||||
| except is_boto3_error_code("NoSuchCORSConfiguration"): | ||||||||||
| current_camel_rules = [] | ||||||||||
| except (BotoCoreError, ClientError) as e: # pylint: disable=duplicate-except | ||||||||||
| module.fail_json_aws(e, msg=f"Unable to get CORS configuration for bucket {name}") | ||||||||||
|
|
||||||||||
| new_camel_rules = snake_dict_to_camel_dict(rules, capitalize_first=True) | ||||||||||
| # compare_policies() takes two dicts and makes them hashable for comparison | ||||||||||
| if compare_policies(new_camel_rules, current_camel_rules): | ||||||||||
|
|
||||||||||
| # Compare normalized rules | ||||||||||
| if normalize_cors_rules(new_camel_rules) != normalize_cors_rules(current_camel_rules): | ||||||||||
| changed = True | ||||||||||
|
|
||||||||||
| if changed: | ||||||||||
| if changed and not module.check_mode: | ||||||||||
| try: | ||||||||||
| connection.put_bucket_cors(Bucket=name, CORSConfiguration={"CORSRules": new_camel_rules}) | ||||||||||
| except (BotoCoreError, ClientError) as e: | ||||||||||
|
|
@@ -133,26 +219,41 @@ def create_or_update_bucket_cors(connection, module): | |||||||||
|
|
||||||||||
|
|
||||||||||
| def destroy_bucket_cors(connection, module): | ||||||||||
| """Remove CORS configuration from an S3 bucket.""" | ||||||||||
| name = module.params.get("name") | ||||||||||
| changed = False | ||||||||||
|
|
||||||||||
| # Check if CORS configuration exists | ||||||||||
| try: | ||||||||||
| connection.delete_bucket_cors(Bucket=name) | ||||||||||
| connection.get_bucket_cors(Bucket=name) | ||||||||||
| # CORS exists, so we need to delete it | ||||||||||
| changed = True | ||||||||||
| except (BotoCoreError, ClientError) as e: | ||||||||||
| module.fail_json_aws(e, msg=f"Unable to delete CORS for bucket {name}") | ||||||||||
| except is_boto3_error_code("NoSuchCORSConfiguration"): | ||||||||||
| # Bucket has no CORS configuration, nothing to delete | ||||||||||
| changed = False | ||||||||||
| except (BotoCoreError, ClientError) as e: # pylint: disable=duplicate-except | ||||||||||
| # Some other error occurred | ||||||||||
| module.fail_json_aws(e, msg=f"Unable to get CORS configuration for bucket {name}") | ||||||||||
|
|
||||||||||
| # Only delete if CORS exists and not in check mode | ||||||||||
| if changed and not module.check_mode: | ||||||||||
| try: | ||||||||||
| connection.delete_bucket_cors(Bucket=name) | ||||||||||
| except (BotoCoreError, ClientError) as e: | ||||||||||
| module.fail_json_aws(e, msg=f"Unable to delete CORS for bucket {name}") | ||||||||||
|
|
||||||||||
| module.exit_json(changed=changed) | ||||||||||
|
|
||||||||||
|
|
||||||||||
| def main(): | ||||||||||
| """Entry point for the s3_cors Ansible module.""" | ||||||||||
| argument_spec = dict( | ||||||||||
| name=dict(required=True, type="str"), | ||||||||||
| rules=dict(type="list", elements="dict"), | ||||||||||
| state=dict(type="str", choices=["present", "absent"], required=True), | ||||||||||
| ) | ||||||||||
|
|
||||||||||
| module = AnsibleAWSModule(argument_spec=argument_spec) | ||||||||||
| module = AnsibleAWSModule(argument_spec=argument_spec, supports_check_mode=True) | ||||||||||
|
|
||||||||||
| client = module.client("s3") | ||||||||||
|
|
||||||||||
|
|
||||||||||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| cloud/aws |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,2 @@ | ||
| --- | ||
| test_bucket: '{{ tiny_prefix }}-s3-cors' |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,2 @@ | ||
| --- | ||
| dependencies: [] |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.