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
Add module for interacting with AWS-hosted Elasticsearch #34277
Conversation
The test
The test
The test
|
The test
|
@Constantin007 @Constantin07 @Deepakkothandan @Etherdaemon @Java1Guy @Lujeni @MichaelBaydoun @Sodki @adq @akazakov @alachaum @amir343 @anryko @bekelchik @bpennypacker @brandond @carsongee @defunctio @dkhenry @fiunchinho @fivethreeo @garethr @gunzy83 @gurumaia @hyperized @iiibrad @infectsoldier @j-carl @jarv @Java1Guy @jimbydamonk @jmenga @joelthompson @jonhadfield @jsdalton @jsmartin @kaczynskid @leedm777 @linuxdynasty @loia @lwade @MichaelBaydoun @michaeljs1990 @minichate @mjschultz @mmochan @nadirollo @nand0p @naslanidis @NickBall @pjodouin @prasadkatti @psykotox @pwnall @raags @rickmendes @roadmapper @ryansydnor @scicoin-project @scottanderson42 @shepdelacreme @silviud @simplesteph @steynovich @tastychutney @tedder @tgerla @timmahoney @tombamford @whiter @wilvk @wimnat @zacblazic @zbal @zeekin @zimbatm As a maintainer of a module in the same namespace this new module has been submitted to, your vote counts for shipits. Please review this module and add |
import time | ||
|
||
from ansible.module_utils.basic import AnsibleModule | ||
from ansible.module_utils import ec2 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You don't need to import everything from ec2. Looks like you're using: ec2_argument_spec, HAS_BOTO3, get_aws_connection_info, boto3_conn
sample: '{"Statement": [{"Action": "*", "Resource": "arn:aws:iam::0000000000000:user/you", "Effect": "Allow"}]}' | ||
""" | ||
|
||
import collections |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You could just import defaultdict
from collections
argument_spec.update( | ||
dict( | ||
domain={'required': True}, | ||
version={'required': False, 'default': '5.1'}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
'required': False
is the default so you can omit that. Same for any of the following.
return modifications_needed | ||
|
||
|
||
def main(): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This module creates domains but doesn't have the ability to remove them. Is this incomplete? I would expect a state
option that allows the choices 'present' or 'absent'.
|
||
region, ec2_url, aws_connect_kwargs = ec2.get_aws_connection_info(module) | ||
|
||
if region: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can remove this check for region as it is now handled in boto3_conn.
arn=domain_status['ARN'], | ||
endpoint=domain_status.get('Endpoint'), | ||
processing=domain_status['Processing'], | ||
elasticsearch_version=domain_status['ElasticsearchVersion'], |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure, since it looks like you're picking info from two result dicts, but the camel_dict_to_snake_dict function might help you out for these lines - you could call that for both dicts and them combine them.
elasticsearch_cluster_config['DedicatedMasterType'] = module.params['dedicated_master_type'] | ||
elasticsearch_cluster_config['DedicatedMasterCount'] = module.params['dedicated_master_count'] | ||
|
||
connection.create_elasticsearch_domain( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This needs to handle ClientError and BotoCoreError. See the guidelines if you haven't already: https://github.com/ansible/ansible/blob/devel/lib/ansible/modules/cloud/amazon/GUIDELINES.md#exception-handling-for-boto3-and-botocore.
ClientError has a .response and BotoCoreError does not. So it could look something like:
try:
connection.create_elasticsearch_domain(...)
except ClientError as e:
module.fail_json(msg="Unable to create elasticsearch domain: {0}".format(to_native(e)),
exception=traceback.format_exc(),
**camel_dict_to_snake_dict(e.response))
except BotoCoreError as e:
module.fail_json(msg="Unable to create elasticsearch domain: {0}".format(to_native(e)),
exception=traceback.format_exc())
That will require import traceback
and from ansible.module_utils.ec2 import camel_dict_to_snake_dict
and from ansible.module_utils._text import to_native
Or:
you can import AnsibleAWSModule from ansible.module_utils.aws.core to replace AnsibleModule, and then you can do:
try:
connection.create_elasticsearch_domain(...)
except (ClientError, BotoCoreError) as e:
module.fail_json_aws(e, msg="Unable to create elasticsearch domain: {0}".format(to_native(e)))
(also needing from ansible.module_utils._text import to_native
)
This would also let you remove the check for HAS_BOTO3 since AnsibleAWSModule handles that. :)
if e.response['Error']['Code'] == 'ResourceNotFoundException': | ||
return None | ||
else: | ||
raise |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Either this should have exception handling that calls fail_json or fail_json_aws (which would need module passed to this function) or everywhere that calls is_present should have the exception handling.
|
||
modification[key_path[-1]] = desired_value | ||
|
||
except Exception as e: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should catch more specific exceptions since as a rule of thumb we try to avoid except Exception
.
existing_config=existing_config, | ||
modifications_needed=modifications_needed) | ||
|
||
# Config is JSON, so compare JSON dictionaries instead of strings |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can compare two dicts with compare_policies() since it fixes comparisons with lists of length one vs a string and different dictionary order:
from ansible.module_utils.ec2 import compare_policies
if compare_policies(existing_access_json, supplied_access_json):
modifications_needed['AccessPolicies'] = module.params['access_policies']
The test
The test
The test
The test
The test
The test
|
Okay! I think I've addressed all the feedback. Thanks for reviewing! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks great. It could also used some integration tets as per https://github.com/ansible/community/blob/master/group-aws/integration.md#policy
# Reload after modifying | ||
domain = is_present(client, module) | ||
|
||
if module.params['tags']: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The provided tags should be compared with the current tags on the resource and only set tags if they are not a subset or equal to those that exist
description: | ||
- JSON string describing the access policy of the cluster. | ||
required: true | ||
wait: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should this have a wait_timeout option to use in conjunction?
zone_awareness: false | ||
instance_type: "t2.medium.elasticsearch" | ||
ebs_volume_size: 10 | ||
access_policies: '{"Statement": [{"Action": "*", "Principal": {"AWS": "arn:aws:iam::0000000000000:user/you"}, "Effect": "Allow"}]}' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since the current policy will always be returned with a 'Version' and this is not specifying version explicitly this will always show changed=True. Can you add Version in this policy to show an example that will be idempotent?
module.fail_json_aws( | ||
e, | ||
msg='Unable to create elasticsearch domain: {0}'.format( | ||
to_native(e)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
e is added to the exception in fail_json_aws so doing it again here will result in a redundant error message. Same for the other exception handling. You could use .format(module.params['domain'])
instead to let the user know which domain failed.
e, | ||
msg='Unexpected error {0}'.format(to_native(e)) | ||
) | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
BotoCoreError could be handled here. Since only ClientError has an e.response, you can do something like
except connection.exceptions.from_code('ResourceNotFoundException') as e:
return None
except (BotoCoreError, ClientError) as e:
module.fail_json_aws(e)
Same for ensure_deleted()
argument_spec=argument_spec, | ||
) | ||
|
||
region, ec2_url, aws_connect_kwargs = get_aws_connection_info( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can replace this line through line 363 with simply: client = module.client('es')
I know this has already name changed once but i think |
I think you need to add a for reference |
description: | ||
- Dictionary of tags to ensure are present on resource. Will not remove other tags. | ||
required: false | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sad to see some of the parameters originally supported disappear such as snapshot hour
argument_spec = ec2_argument_spec() | ||
argument_spec.update( | ||
dict( | ||
domain={'required': True}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is more common in ansible modules to use:
domain=dict(required=True)
'default': 'gp2', 'choices': ['standard', 'gp2', 'io1'] | ||
}, | ||
ebs_volume_size={'required': True, 'type': 'int'}, | ||
access_policies={'required': True}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should have a type of 'json'
) | ||
|
||
if module.params['state'] not in ['present', 'absent']: | ||
module.fail_json(msg='"state" must be "present" or "absent"') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This statement is not required as it's handled by Ansible parameter checking... you've already specified the choices.
|
||
module.exit_json( | ||
changed=changed, | ||
arn=domain_status['ARN'], |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's more common to use the helper camel_dict_to_snake_dict here https://github.com/ansible/ansible/blob/devel/lib/ansible/modules/cloud/amazon/GUIDELINES.md#camel_dict_to_snake_dict.
You can just do camel_dict_to_snake_dict(**domain_status)
I am interested about this module. Is there anything left to do? |
@alinalexandru testing this module locally and reporting back if it works would be really helpful. The common reason that PRs don't get merged is due to lack of feedback. |
The test
|
Please submit a new module request to https://github.com/ansible-collections/community.aws. Thanks for your contribution. |
SUMMARY
I've been using this to manage AWS-hosted Elasticsearch and thought I would post it to see if it would be useful to others.
I haven't submitted a PR for ansible before, but I did read over the Contributing pages - feedback welcome!
ISSUE TYPE
COMPONENT NAME
cloud/amazon/elasticsearch.py
ANSIBLE VERSION
ADDITIONAL INFORMATION
N/A