Beispiel #1
0
def update_route_tables(target_peerings: list, metadata: Mapping,
                        dryrun: bool) -> None:
    """
    Loops through a list of peerings and updates the route tables on each side.

    Example target_peerings:
    ```
    [
        [
        {
            "account_id": 415432961280,
            "vpc_id": "vpc-e08fb484",
            "region": "ap-southeast-2",
            "cidr_overrides": [
                "10.53.101.0/27"
            ],
            "peering_tags": [
                {
                "peerd_az_affinity": "0"
                }
            ]
        },
        {
            "account_id": 415432961280,
            "vpc_id": "vpc-7a83b81e",
            "region": "ap-southeast-2"
        }
        ]
    ]
    ```

    :param target_peerings: A list of lists representing the requester and accepter for each peering.
    :type target_peerings: list
    :param metadata: A dictionary with the environment, owner, etc for tagging
    :type metadata: list
    """

    # We need to handle both sides of the peerings so we append reverse of each peering.
    # This means every side of every peering will be seen by a single loop.
    # We do this to avoid extra code handling the AWS concept of "accepter" and "requester"
    # We also construct our route table cache by account and region, which means if we looped
    # by requester and accepter we could cache stale route table contents.
    target_peerings.extend([x[::-1] for x in target_peerings])

    # Loop through the target peerings
    for peering_descriptor in target_peerings:
        # Unpack some common variables
        account_id = peering_descriptor[0]['account_id']
        vpc_id = peering_descriptor[0]['vpc_id']
        region = peering_descriptor[0]['region']

        remote_account_id = peering_descriptor[1]['account_id']
        remote_vpc_id = peering_descriptor[1]['vpc_id']
        remote_region = peering_descriptor[1]['region']
        # Get the remote CIDRs from the AWS VPC API, or use the overrides in the config if they exist.
        remote_cidrs = peering_descriptor[1].get(
            'cidr_overrides',
            list_vpc_cidrs(remote_vpc_id, remote_account_id, remote_region))

        LOGGER.info(
            f"Inspecting route tables in {account_id} {vpc_id} "
            f"{region}, peer: {remote_account_id} {remote_vpc_id} {remote_region}"
        )

        # Initialise a ec2 client connection
        ec2_client = aws_client(account_id, 'ec2', region)

        # Get active VPC peering if one exists, else continue
        # We want to avoid adding routes for inactive peerings.
        filters = [{
            'Name': 'tag:peerd_created',
            'Values': ['true']
        }, {
            'Name': 'tag:peerd_environment',
            'Values': [metadata['environment']]
        }, {
            'Name': 'status-code',
            'Values': ['active', 'provisioning']
        }]
        if not (peering := get_vpc_peering(vpc_id, remote_vpc_id, account_id,
                                           region, filters)):
            # Since we filter for the peerd environment, remind the user that there could be a peering
            # But that it might exist as part of another environment, and thus we won't be touching it.
            LOGGER.warning(
                f'No active peering between {vpc_id} and {remote_vpc_id} for this environment'
                f' {metadata["environment"]}. It may exist as part of another environment.'
            )
            continue
        peering_id = peering['VpcPeeringConnectionId']

        # Wait until the peering is active, not provisioning (boto waiter doesn't accept filters for vpc peering api)
        # We must wait for active state to install routes, otherwise the peering will be ignored.
        # Note, usually this step takes a few seconds, but can sometimes take up to a minute or two in rare cases.
        if peering['Status']['Code'] == 'provisioning':
            while not ec2_client.describe_vpc_peering_connections(
                    Filters=[{
                        'Name': 'status-code',
                        'Values': ['active']
                    }, {
                        'Name': 'vpc-peering-connection-id',
                        'Values': [peering_id]
                    }])['VpcPeeringConnections']:
                LOGGER.info(
                    f'Waiting for peering {peering_id} to become active...')
                sleep(5)

        # Get the route tables for the local vpc relevant to the peering.
        # The vpc_route_tables function will only return peerd_eligible:true tables
        # Since we will have cases where RTs should not be altered.
        if not (route_tables := vpc_route_tables(vpc_id, account_id, region)):
            LOGGER.warning(f'No peerd_eligible route tables in VPC {vpc_id}')
            continue
Beispiel #2
0
def create_vpc_peerings(target_peerings: Sequence, metadata: Mapping,
                        dryrun: bool) -> None:
    """
    Loops through a list of peerings to create them.
    Requests, accepts and tags them.
    Repairs any half open peerings.

    Example target_peerings:
    ```
    [
        [
        {
            "account_id": 415432961280,
            "vpc_id": "vpc-e08fb484",
            "region": "ap-southeast-2",
            "cidr_overrides": [
                "10.53.101.0/27"
            ],
            "peering_tags": [
                {
                "peerd_az_affinity": "0"
                }
            ]
        },
        {
            "account_id": 415432961280,
            "vpc_id": "vpc-7a83b81e",
            "region": "ap-southeast-2"
        }
        ]
    ]
    ```

    :param target_peerings: A list of lists representing the requester and accepter for each peering.
    :type target_peerings: list
    :param metadata: A dictionary with the environment, owner, etc for tagging
    :type metadata: list
    """
    pending_acceptance_peerings = []
    for peering_descriptor in target_peerings:

        # Unpack some common variables
        account_id = peering_descriptor[0]['account_id']
        vpc_id = peering_descriptor[0]['vpc_id']
        region = peering_descriptor[0]['region']
        local_tags = peering_descriptor[0].get('peering_tags', {})

        remote_account_id = peering_descriptor[1]['account_id']
        remote_vpc_id = peering_descriptor[1]['vpc_id']
        remote_region = peering_descriptor[1]['region']
        remote_tags = peering_descriptor[1].get('peering_tags', {})

        # Create a VPC peering request
        try:
            ec2_client = aws_client(account_id, 'ec2', region)
            # If the peering doesn't exist, create it
            if not (peering := get_vpc_peering(vpc_id, remote_vpc_id,
                                               account_id, region)):
                LOGGER.info(
                    f"Creating peering request between {account_id} {vpc_id} {region}"
                    f" and {remote_account_id} {remote_vpc_id} {remote_region}"
                )
                try:
                    ec2_peering_response = ec2_client.create_vpc_peering_connection(
                        VpcId=vpc_id,
                        PeerVpcId=remote_vpc_id,
                        PeerOwnerId=remote_account_id,
                        PeerRegion=remote_region,
                        DryRun=dryrun)['VpcPeeringConnection']
                except ClientError as err:
                    if err.response['Error']['Code'] == 'DryRunOperation':
                        continue
                    raise
                peering_id = ec2_peering_response['VpcPeeringConnectionId']
                # Wait for the vpc peering to exist before moving on
                LOGGER.info(f'Waiting for peering {peering_id} to exist...')
                ec2_client.get_waiter('vpc_peering_connection_exists').wait(
                    VpcPeeringConnectionIds=[peering_id],
                    WaiterConfig={'Delay': 5})
            # If the peering exists and is active, do nothing.
            elif peering['Status']['Code'] == 'active':
                LOGGER.info(
                    f"Active peering {peering['VpcPeeringConnectionId']} between {account_id} {vpc_id} {region}"
                    f" and {remote_account_id} {remote_vpc_id} {remote_region}"
                )
                continue
            # If the peering is pending acceptance move to tagging and acceptance
            # Only the remote account can accept the VPC peering.
            elif peering['Status']['Code'] == 'pending-acceptance' and peering[
                    'RequesterVpcInfo']['VpcId'] == vpc_id:
                peering_id = peering['VpcPeeringConnectionId']
                LOGGER.warning(
                    f"Pending-Acceptance peering {peering_id} between {account_id} {vpc_id} {region}"
                    f" and {remote_account_id} {remote_vpc_id} {remote_region}. Will attempt recovery."
                )