From 1e8488830cf8501e515d5b876c5c510e901b6e3c Mon Sep 17 00:00:00 2001 From: Nicolas Roche Date: Mon, 12 Mar 2018 14:42:52 +0100 Subject: [PATCH] Add support for VPCEndpoint. --- e3/aws/cfn/__init__.py | 21 ++++++++++++ e3/aws/cfn/ec2/__init__.py | 43 ++++++++++++++++++++++++- tests/tests_e3_aws/cfn/ec2/main_test.py | 10 +++++- 3 files changed, 72 insertions(+), 2 deletions(-) diff --git a/e3/aws/cfn/__init__.py b/e3/aws/cfn/__init__.py index e11ef65a..b5b40e34 100644 --- a/e3/aws/cfn/__init__.py +++ b/e3/aws/cfn/__init__.py @@ -21,6 +21,7 @@ class AWSType(Enum): 'AWS::EC2::SubnetRouteTableAssociation' EC2_VOLUME = 'AWS::EC2::Volume' EC2_VPC = 'AWS::EC2::VPC' + EC2_VPC_ENDPOINT = 'AWS::EC2::VPC_ENDPOINT' EC2_VPC_GATEWAY_ATTACHMENT = 'AWS::EC2::VPCGatewayAttachment' IAM_ROLE = 'AWS::IAM::Role' IAM_POLICY = 'AWS::IAM::Policy' @@ -68,6 +69,21 @@ def __init__(self, content): self.content = content +class Join(object): + """Intrinsic function Fn::Join.""" + + def __init__(self, content, delimiter=""): + """Initialize a Join object. + + :param content: a list + :type content: list + :param delimiter: a join delimiter + :type delimiter: str + """ + self.content = content + self.delimiter = delimiter + + # Declare Yaml representer for intrinsic functions def getatt_representer(dumper, data): @@ -83,9 +99,14 @@ def base64_representer(dumper, data): return dumper.represent_scalar('!Base64', data.content) +def join_representer(dumper, data): + return dumper.represent_sequence('!Join', [data.delimiter, data.content]) + + yaml.add_representer(GetAtt, getatt_representer) yaml.add_representer(Ref, ref_representer) yaml.add_representer(Base64, base64_representer) +yaml.add_representer(Join, join_representer) class Resource(object): diff --git a/e3/aws/cfn/ec2/__init__.py b/e3/aws/cfn/ec2/__init__.py index 8c0f9d16..448a421d 100644 --- a/e3/aws/cfn/ec2/__init__.py +++ b/e3/aws/cfn/ec2/__init__.py @@ -2,7 +2,8 @@ from email.contentmanager import raw_data_manager from email.message import EmailMessage -from e3.aws.cfn import Resource, AWSType, GetAtt, Base64 +from e3.aws.cfn import Resource, AWSType, GetAtt, Base64, Join, Ref +from e3.aws.cfn.iam import PolicyDocument from e3.aws.ec2.ami import AMI @@ -297,6 +298,46 @@ def cidrblock(self): return self.getatt('CidrBlock') +class VPCEndpoint(Resource): + """VPC Endpoint to Amazon Service.""" + + def __init__(self, name, service, vpc, route_tables, policy_document): + """Initialize a VPC endpoint. + + :param name: logical name in the stack of the entity + :type name: str + :param service: name of the service to connect (s3 or dynamodb) + :type service: str + :param vpc: VPC in which the endpoint is attached to + :type vpc: e3.aws.cfn.ec2.VPC + :param route_tables: a list of route table that have access to the + endpoint. + :type route_tables: list[RouteTable] + :param policy_document: policy document attached to the endpoint. + :type policy_docyment: e3.aws.cfn.ec2.security.PolicyDocument + """ + super(VPCEndpoint, self).__init__(name, kind=AWSType.EC2_VPC_ENDPOINT) + assert service in ('dynamodb', 's3'), 'Invalid service: %s' % service + self.service = service + assert isinstance(vpc, VPC), 'VPC instance expected' + self.vpc = vpc + self.route_tables = route_tables + for rt in self.route_tables: + assert isinstance(rt, RouteTable), 'RouteTable expected' + self.policy_document = policy_document + assert isinstance(self.policy_document, PolicyDocument) + + @property + def properties(self): + return { + 'VpcId': self.vpc.ref, + 'ServiceName': Join(["com.amazonaws.", + Ref("AWS::Region"), + "." + self.service]), + 'PolicyDocument': self.policy_document.properties, + 'RouteTableIds': [rt.ref for rt in self.route_tables]} + + class Subnet(Resource): """EC2 subnet.""" diff --git a/tests/tests_e3_aws/cfn/ec2/main_test.py b/tests/tests_e3_aws/cfn/ec2/main_test.py index a2842e81..a9222bdd 100644 --- a/tests/tests_e3_aws/cfn/ec2/main_test.py +++ b/tests/tests_e3_aws/cfn/ec2/main_test.py @@ -8,8 +8,9 @@ from e3.aws.cfn.ec2 import (VPC, EphemeralDisk, Instance, InternetGateway, NetworkInterface, Route, RouteTable, Subnet, SubnetRouteTableAssociation, UserData, - VPCGatewayAttachment) + VPCEndpoint, VPCGatewayAttachment) from e3.aws.cfn.ec2.security import SecurityGroup +from e3.aws.cfn.iam import Allow, PolicyDocument from e3.aws.ec2.ami import AMI @@ -32,6 +33,13 @@ def test_create_network(): s += SubnetRouteTableAssociation('RTSAssoc', s['BuildPublicSubnet'], s['RT']) + p = PolicyDocument().append(Allow(to='GetObject', + on='arn:aws:s3:::abucket/*', + service='ec2.amazonaws.com')) + + s += VPCEndpoint('S3EndPoint', + 's3', s['BuildVPC'], [s['RT']], + policy_document=p) assert s.body