Source code for idem_aws.states.aws.route53.resource_record

"""State module for managing Route53 Resource records."""
import copy
from typing import Any
from typing import Dict
from typing import List

__contracts__ = ["resource"]


[docs]async def present( hub, ctx, name: str, hosted_zone_id: str, record_type: str, resource_records: List[str] = None, alias_target: Dict = None, resource_id: str = None, ttl: int = None, ) -> Dict[str, Any]: """Creates or changes a resource record set, which contains authoritative DNS information for a specified domain name or subdomain name. Args: name(str): The name of the record. A (``.``) will be appended if not already present. hosted_zone_id(str): The ID of the hosted zone that contains the resource record sets. record_type(str): The DNS record type. For information about different record types and how data is encoded for them, see `Supported DNS Resource Record Types` https://docs.aws.amazon.com/Route53/latest/DeveloperGuide/ResourceRecordTypes.html in the Amazon Route 53 Developer Guide. resource_records(list, Optional): Information about the resource records to act upon. alias_target(dict, Optional): Alias resource record sets only: Information about the Amazon Web Services resource, such as a CloudFront distribution or an Amazon S3 bucket, that you want to route traffic to. resource_id(str, Optional): Composite ID for a resource record in a hosted zone. String formatted as <hosted_zone_id>_<record_name>_<record_type>. ttl(int, Optional): The resource record cache time to live (TTL), in seconds. Returns: Dict[str, Any] Examples: .. code-block:: sls KL1PX9DBMUY9WHB_www.example.com_AAAA: aws.route53.resource_record.present: - hosted_zone_id: /hostedzone/KL1PX9DBMUY9WHB - name: www.example.com. - record_type: AAAA - resource_records: - 2001:0db8:85a3:0:0:8a2e:0370:7335 - 2001:0db8:85a3:0:0:8a2e:0370:7334 - ttl: 300 ZY51FUS5VYB_www.example.net_A: aws.route53.resource_record.present: - alias_target: dns_name: lb1.us-east-1.elb.amazonaws.com. evaluate_target_health: false hosted_zone_id: Z35SXDOTRQ7X7Z - hosted_zone_id: /hostedzone/ZY51FUS5VYB - name: www.example.net. - record_type: A - resource_id: ZY51FUS5VYB_www.example.net_A """ result = dict(comment=[], old_state=None, new_state=None, name=name, result=True) # name is supposed to end with '.'. # Because name is used when we look up the resource record we should store the canonical form in the state formatted_name = name if name.endswith(".") else f"{name}." if resource_id: if ( hub.tool.aws.route53.resource_record_utils.compose_resource_id( hosted_zone_id, name, record_type ) != resource_id ): result["comment"] = ( f"aws.route53.resource_record resource_id {resource_id} is not composed of {hosted_zone_id}, {name} and {record_type}", ) result["result"] = False return result before_response = ( await hub.tool.aws.route53.resource_record_utils.find_resource_record( ctx, hosted_zone_id, formatted_name, record_type ) ) if not before_response["result"]: result["comment"] = before_response["comment"] result["result"] = False return result before = before_response.get("aws_state") pending_state = None if before: result[ "old_state" ] = hub.tool.aws.route53.conversion_utils.convert_raw_resource_record_to_present( hosted_zone_id=hosted_zone_id, raw_resource=before ) pending_state = hub.tool.aws.route53.resource_record_utils.patch_state( before, hosted_zone_id, ttl, resource_records, alias_target ) else: pending_state = { "name": formatted_name, "hosted_zone_id": hosted_zone_id, "resource_id": resource_id if resource_id else hub.tool.aws.route53.resource_record_utils.compose_resource_id( hosted_zone_id, formatted_name, record_type ), "record_type": record_type, } if ttl: pending_state["ttl"] = ttl if resource_records: pending_state["resource_records"] = resource_records if alias_target: pending_state["alias_target"] = alias_target # action can be 'CREATE'|'DELETE'|'UPSERT' - see boto documentation for change_resource_record_sets action = None if before: if not hub.tool.aws.route53.resource_record_utils.same_states( pending_state, result["old_state"] ): result["comment"] = hub.tool.aws.comment_utils.would_update_comment( resource_type="aws.route53.resource_record", name=formatted_name ) action = "UPSERT" else: result["comment"] += [ f"No changes would be made for aws.route53.resource_record {formatted_name}" ] result["result"] = True else: result["comment"] = hub.tool.aws.comment_utils.would_create_comment( resource_type="aws.route53.resource_record", name=formatted_name ) action = "CREATE" result["result"] = True if ctx.get("test", False): result["new_state"] = pending_state return result # do the actual update if action: change_batch = ( hub.tool.aws.route53.resource_record_utils.create_change_batch_for_update( action, pending_state ) ) response = await hub.exec.boto3.client.route53.change_resource_record_sets( ctx, HostedZoneId=hosted_zone_id, ChangeBatch=change_batch ) if not response["result"]: result["comment"] += [ f'Error on {action} for aws.route53.resource_record {formatted_name}: {response["comment"]}' ] result["result"] = False return result result["new_state"] = pending_state if action == "CREATE": result["comment"] = hub.tool.aws.comment_utils.create_comment( resource_type="aws.route53.resource_record", name=formatted_name ) else: result["comment"] = hub.tool.aws.comment_utils.update_comment( resource_type="aws.route53.resource_record", name=formatted_name ) else: result["new_state"] = copy.deepcopy(result["old_state"]) return result
[docs]async def absent(hub, ctx, name: str, resource_id: str = None) -> Dict[str, Any]: """Deletes the specified resource record Args: name(str): Name of the resource record. Needed because of the Idem contract but not used. resource_id(str, Optional): Composite ID for a resource record in a hosted zone. String formatted as <hosted_zone_id>_<record_name>_<record_type>. Returns: Dict[str, Any] Examples: .. code-block:: sls MX_record_is_absent: aws.route53.resource_record.absent: - name: www.example.com. - resource_id: HSHMRK8IGWBU3PU_www.example.com_MX """ result = dict(comment=[], old_state=None, new_state=None, name=name, result=True) if not resource_id: result["comment"] = hub.tool.aws.comment_utils.already_absent_comment( resource_type="aws.route53.resource_record", name=name ) return result try: decomposed_id = ( hub.tool.aws.route53.resource_record_utils.decompose_resource_id( resource_id ) ) except Exception as e: result["comment"] += [f"{e.__class__.__name__}: {e}"] result["result"] = False return result hosted_zone_id = decomposed_id["hosted_zone_id"] formatted_name = f"{decomposed_id['name']}" record_type = decomposed_id["record_type"] before_response = ( await hub.tool.aws.route53.resource_record_utils.find_resource_record( ctx, hosted_zone_id, formatted_name, record_type, ) ) if not before_response["result"]: result["comment"] = before_response["comment"] result["result"] = False return result before = before_response.get("aws_state") if not before: result["comment"] = hub.tool.aws.comment_utils.already_absent_comment( resource_type="aws.route53.resource_record", name=formatted_name ) else: result[ "old_state" ] = hub.tool.aws.route53.conversion_utils.convert_raw_resource_record_to_present( hosted_zone_id=hosted_zone_id, raw_resource=before ) result["comment"] = hub.tool.aws.comment_utils.would_delete_comment( resource_type="aws.route53.resource_record", name=formatted_name ) if ctx.get("test", False): return result # delete the resource record change_batch = ( hub.tool.aws.route53.resource_record_utils.create_change_batch_for_update( "DELETE", result["old_state"] ) ) response = await hub.exec.boto3.client.route53.change_resource_record_sets( ctx, HostedZoneId=hosted_zone_id, ChangeBatch=change_batch, ) if not response["result"]: result["comment"] += [ f"Error deleting aws.route53.resource_record '{formatted_name}': {response['comment']}" ] result["result"] = False return result result["comment"] = hub.tool.aws.comment_utils.delete_comment( resource_type="aws.route53.resource_record", name=formatted_name ) return result
[docs]async def describe(hub, ctx) -> Dict[str, Dict[str, Any]]: """ Describe the resource in a way that can be recreated/managed with the corresponding "present" function Lists the resource record sets in a specified hosted zone. Returns: Dict[str, Any] Examples: .. code-block:: bash $ idem describe aws.route53.resource_record """ result = {} hosted_zones = await hub.exec.boto3.client.route53.list_hosted_zones(ctx) if not hosted_zones["result"]: hub.log.warning( f"Could not describe aws.route53.resource_record. {hosted_zones['comment']}" ) return result for hosted_zone in hosted_zones["ret"]["HostedZones"]: hosted_zone_id = hosted_zone.get("Id") ret = await hub.exec.boto3.client.route53.list_resource_record_sets( ctx, HostedZoneId=hosted_zone_id ) if not ret["result"]: hub.log.warning( f"Could not describe aws.route53.hosted_zone {ret['comment']}" ) continue for resource_record_set in ret["ret"]["ResourceRecordSets"]: resource_translated = hub.tool.aws.route53.conversion_utils.convert_raw_resource_record_to_present( hosted_zone_id=hosted_zone_id, raw_resource=resource_record_set ) result[resource_translated["resource_id"]] = { "aws.route53.resource_record.present": [ {parameter_key: parameter_value} for parameter_key, parameter_value in resource_translated.items() ] } return result