Merge pull request #2401 from rocky4570/redshift-enhanced-vpc-routing

add enhanced vpc routing option to redshift moto
This commit is contained in:
Steve Pulec 2019-09-11 21:40:12 -05:00 committed by GitHub
commit eb5bf2ed55
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 97 additions and 7 deletions

View File

@ -74,7 +74,7 @@ class Cluster(TaggableResourceMixin, BaseModel):
automated_snapshot_retention_period, port, cluster_version, automated_snapshot_retention_period, port, cluster_version,
allow_version_upgrade, number_of_nodes, publicly_accessible, allow_version_upgrade, number_of_nodes, publicly_accessible,
encrypted, region_name, tags=None, iam_roles_arn=None, encrypted, region_name, tags=None, iam_roles_arn=None,
restored_from_snapshot=False): enhanced_vpc_routing=None, restored_from_snapshot=False):
super(Cluster, self).__init__(region_name, tags) super(Cluster, self).__init__(region_name, tags)
self.redshift_backend = redshift_backend self.redshift_backend = redshift_backend
self.cluster_identifier = cluster_identifier self.cluster_identifier = cluster_identifier
@ -85,6 +85,7 @@ class Cluster(TaggableResourceMixin, BaseModel):
self.master_user_password = master_user_password self.master_user_password = master_user_password
self.db_name = db_name if db_name else "dev" self.db_name = db_name if db_name else "dev"
self.vpc_security_group_ids = vpc_security_group_ids self.vpc_security_group_ids = vpc_security_group_ids
self.enhanced_vpc_routing = enhanced_vpc_routing if enhanced_vpc_routing is not None else False
self.cluster_subnet_group_name = cluster_subnet_group_name self.cluster_subnet_group_name = cluster_subnet_group_name
self.publicly_accessible = publicly_accessible self.publicly_accessible = publicly_accessible
self.encrypted = encrypted self.encrypted = encrypted
@ -154,6 +155,7 @@ class Cluster(TaggableResourceMixin, BaseModel):
port=properties.get('Port'), port=properties.get('Port'),
cluster_version=properties.get('ClusterVersion'), cluster_version=properties.get('ClusterVersion'),
allow_version_upgrade=properties.get('AllowVersionUpgrade'), allow_version_upgrade=properties.get('AllowVersionUpgrade'),
enhanced_vpc_routing=properties.get('EnhancedVpcRouting'),
number_of_nodes=properties.get('NumberOfNodes'), number_of_nodes=properties.get('NumberOfNodes'),
publicly_accessible=properties.get("PubliclyAccessible"), publicly_accessible=properties.get("PubliclyAccessible"),
encrypted=properties.get("Encrypted"), encrypted=properties.get("Encrypted"),
@ -241,6 +243,7 @@ class Cluster(TaggableResourceMixin, BaseModel):
'ClusterCreateTime': self.create_time, 'ClusterCreateTime': self.create_time,
"PendingModifiedValues": [], "PendingModifiedValues": [],
"Tags": self.tags, "Tags": self.tags,
"EnhancedVpcRouting": self.enhanced_vpc_routing,
"IamRoles": [{ "IamRoles": [{
"ApplyStatus": "in-sync", "ApplyStatus": "in-sync",
"IamRoleArn": iam_role_arn "IamRoleArn": iam_role_arn
@ -427,6 +430,7 @@ class Snapshot(TaggableResourceMixin, BaseModel):
'NumberOfNodes': self.cluster.number_of_nodes, 'NumberOfNodes': self.cluster.number_of_nodes,
'DBName': self.cluster.db_name, 'DBName': self.cluster.db_name,
'Tags': self.tags, 'Tags': self.tags,
'EnhancedVpcRouting': self.cluster.enhanced_vpc_routing,
"IamRoles": [{ "IamRoles": [{
"ApplyStatus": "in-sync", "ApplyStatus": "in-sync",
"IamRoleArn": iam_role_arn "IamRoleArn": iam_role_arn
@ -678,7 +682,8 @@ class RedshiftBackend(BaseBackend):
"number_of_nodes": snapshot.cluster.number_of_nodes, "number_of_nodes": snapshot.cluster.number_of_nodes,
"encrypted": snapshot.cluster.encrypted, "encrypted": snapshot.cluster.encrypted,
"tags": snapshot.cluster.tags, "tags": snapshot.cluster.tags,
"restored_from_snapshot": True "restored_from_snapshot": True,
"enhanced_vpc_routing": snapshot.cluster.enhanced_vpc_routing
} }
create_kwargs.update(kwargs) create_kwargs.update(kwargs)
return self.create_cluster(**create_kwargs) return self.create_cluster(**create_kwargs)

View File

@ -135,6 +135,7 @@ class RedshiftResponse(BaseResponse):
"region_name": self.region, "region_name": self.region,
"tags": self.unpack_complex_list_params('Tags.Tag', ('Key', 'Value')), "tags": self.unpack_complex_list_params('Tags.Tag', ('Key', 'Value')),
"iam_roles_arn": self._get_iam_roles(), "iam_roles_arn": self._get_iam_roles(),
"enhanced_vpc_routing": self._get_param('EnhancedVpcRouting'),
} }
cluster = self.redshift_backend.create_cluster(**cluster_kwargs).to_json() cluster = self.redshift_backend.create_cluster(**cluster_kwargs).to_json()
cluster['ClusterStatus'] = 'creating' cluster['ClusterStatus'] = 'creating'
@ -150,6 +151,7 @@ class RedshiftResponse(BaseResponse):
}) })
def restore_from_cluster_snapshot(self): def restore_from_cluster_snapshot(self):
enhanced_vpc_routing = self._get_bool_param('EnhancedVpcRouting')
restore_kwargs = { restore_kwargs = {
"snapshot_identifier": self._get_param('SnapshotIdentifier'), "snapshot_identifier": self._get_param('SnapshotIdentifier'),
"cluster_identifier": self._get_param('ClusterIdentifier'), "cluster_identifier": self._get_param('ClusterIdentifier'),
@ -171,6 +173,8 @@ class RedshiftResponse(BaseResponse):
"region_name": self.region, "region_name": self.region,
"iam_roles_arn": self._get_iam_roles(), "iam_roles_arn": self._get_iam_roles(),
} }
if enhanced_vpc_routing is not None:
restore_kwargs['enhanced_vpc_routing'] = enhanced_vpc_routing
cluster = self.redshift_backend.restore_from_cluster_snapshot(**restore_kwargs).to_json() cluster = self.redshift_backend.restore_from_cluster_snapshot(**restore_kwargs).to_json()
cluster['ClusterStatus'] = 'creating' cluster['ClusterStatus'] = 'creating'
return self.get_response({ return self.get_response({
@ -218,6 +222,7 @@ class RedshiftResponse(BaseResponse):
"publicly_accessible": self._get_param("PubliclyAccessible"), "publicly_accessible": self._get_param("PubliclyAccessible"),
"encrypted": self._get_param("Encrypted"), "encrypted": self._get_param("Encrypted"),
"iam_roles_arn": self._get_iam_roles(), "iam_roles_arn": self._get_iam_roles(),
"enhanced_vpc_routing": self._get_param("EnhancedVpcRouting")
} }
cluster_kwargs = {} cluster_kwargs = {}
# We only want parameters that were actually passed in, otherwise # We only want parameters that were actually passed in, otherwise

View File

@ -37,6 +37,25 @@ def test_create_cluster_boto3():
create_time = response['Cluster']['ClusterCreateTime'] create_time = response['Cluster']['ClusterCreateTime']
create_time.should.be.lower_than(datetime.datetime.now(create_time.tzinfo)) create_time.should.be.lower_than(datetime.datetime.now(create_time.tzinfo))
create_time.should.be.greater_than(datetime.datetime.now(create_time.tzinfo) - datetime.timedelta(minutes=1)) create_time.should.be.greater_than(datetime.datetime.now(create_time.tzinfo) - datetime.timedelta(minutes=1))
response['Cluster']['EnhancedVpcRouting'].should.equal(False)
@mock_redshift
def test_create_cluster_boto3():
client = boto3.client('redshift', region_name='us-east-1')
response = client.create_cluster(
DBName='test',
ClusterIdentifier='test',
ClusterType='single-node',
NodeType='ds2.xlarge',
MasterUsername='user',
MasterUserPassword='password',
EnhancedVpcRouting=True
)
response['Cluster']['NodeType'].should.equal('ds2.xlarge')
create_time = response['Cluster']['ClusterCreateTime']
create_time.should.be.lower_than(datetime.datetime.now(create_time.tzinfo))
create_time.should.be.greater_than(datetime.datetime.now(create_time.tzinfo) - datetime.timedelta(minutes=1))
response['Cluster']['EnhancedVpcRouting'].should.equal(True)
@mock_redshift @mock_redshift
@ -425,6 +444,58 @@ def test_delete_cluster():
"not-a-cluster").should.throw(ClusterNotFound) "not-a-cluster").should.throw(ClusterNotFound)
@mock_redshift
def test_modify_cluster_vpc_routing():
iam_roles_arn = ['arn:aws:iam:::role/my-iam-role', ]
client = boto3.client('redshift', region_name='us-east-1')
cluster_identifier = 'my_cluster'
client.create_cluster(
ClusterIdentifier=cluster_identifier,
NodeType="single-node",
MasterUsername="username",
MasterUserPassword="password",
IamRoles=iam_roles_arn
)
cluster_response = client.describe_clusters(ClusterIdentifier=cluster_identifier)
cluster = cluster_response['Clusters'][0]
cluster['EnhancedVpcRouting'].should.equal(False)
client.create_cluster_security_group(ClusterSecurityGroupName='security_group',
Description='security_group')
client.create_cluster_parameter_group(ParameterGroupName='my_parameter_group',
ParameterGroupFamily='redshift-1.0',
Description='my_parameter_group')
client.modify_cluster(
ClusterIdentifier=cluster_identifier,
ClusterType='multi-node',
NodeType="ds2.8xlarge",
NumberOfNodes=3,
ClusterSecurityGroups=["security_group"],
MasterUserPassword="new_password",
ClusterParameterGroupName="my_parameter_group",
AutomatedSnapshotRetentionPeriod=7,
PreferredMaintenanceWindow="Tue:03:00-Tue:11:00",
AllowVersionUpgrade=False,
NewClusterIdentifier=cluster_identifier,
EnhancedVpcRouting=True
)
cluster_response = client.describe_clusters(ClusterIdentifier=cluster_identifier)
cluster = cluster_response['Clusters'][0]
cluster['ClusterIdentifier'].should.equal(cluster_identifier)
cluster['NodeType'].should.equal("ds2.8xlarge")
cluster['PreferredMaintenanceWindow'].should.equal("Tue:03:00-Tue:11:00")
cluster['AutomatedSnapshotRetentionPeriod'].should.equal(7)
cluster['AllowVersionUpgrade'].should.equal(False)
# This one should remain unmodified.
cluster['NumberOfNodes'].should.equal(3)
cluster['EnhancedVpcRouting'].should.equal(True)
@mock_redshift_deprecated @mock_redshift_deprecated
def test_modify_cluster(): def test_modify_cluster():
conn = boto.connect_redshift() conn = boto.connect_redshift()
@ -446,6 +517,10 @@ def test_modify_cluster():
master_user_password="password", master_user_password="password",
) )
cluster_response = conn.describe_clusters(cluster_identifier)
cluster = cluster_response['DescribeClustersResponse']['DescribeClustersResult']['Clusters'][0]
cluster['EnhancedVpcRouting'].should.equal(False)
conn.modify_cluster( conn.modify_cluster(
cluster_identifier, cluster_identifier,
cluster_type="multi-node", cluster_type="multi-node",
@ -456,14 +531,13 @@ def test_modify_cluster():
automated_snapshot_retention_period=7, automated_snapshot_retention_period=7,
preferred_maintenance_window="Tue:03:00-Tue:11:00", preferred_maintenance_window="Tue:03:00-Tue:11:00",
allow_version_upgrade=False, allow_version_upgrade=False,
new_cluster_identifier="new_identifier", new_cluster_identifier=cluster_identifier,
) )
cluster_response = conn.describe_clusters("new_identifier") cluster_response = conn.describe_clusters(cluster_identifier)
cluster = cluster_response['DescribeClustersResponse'][ cluster = cluster_response['DescribeClustersResponse'][
'DescribeClustersResult']['Clusters'][0] 'DescribeClustersResult']['Clusters'][0]
cluster['ClusterIdentifier'].should.equal(cluster_identifier)
cluster['ClusterIdentifier'].should.equal("new_identifier")
cluster['NodeType'].should.equal("dw.hs1.xlarge") cluster['NodeType'].should.equal("dw.hs1.xlarge")
cluster['ClusterSecurityGroups'][0][ cluster['ClusterSecurityGroups'][0][
'ClusterSecurityGroupName'].should.equal("security_group") 'ClusterSecurityGroupName'].should.equal("security_group")
@ -674,6 +748,7 @@ def test_create_cluster_snapshot():
NodeType='ds2.xlarge', NodeType='ds2.xlarge',
MasterUsername='username', MasterUsername='username',
MasterUserPassword='password', MasterUserPassword='password',
EnhancedVpcRouting=True
) )
cluster_response['Cluster']['NodeType'].should.equal('ds2.xlarge') cluster_response['Cluster']['NodeType'].should.equal('ds2.xlarge')
@ -823,11 +898,14 @@ def test_create_cluster_from_snapshot():
NodeType='ds2.xlarge', NodeType='ds2.xlarge',
MasterUsername='username', MasterUsername='username',
MasterUserPassword='password', MasterUserPassword='password',
EnhancedVpcRouting=True,
) )
client.create_cluster_snapshot( client.create_cluster_snapshot(
SnapshotIdentifier=original_snapshot_identifier, SnapshotIdentifier=original_snapshot_identifier,
ClusterIdentifier=original_cluster_identifier ClusterIdentifier=original_cluster_identifier
) )
response = client.restore_from_cluster_snapshot( response = client.restore_from_cluster_snapshot(
ClusterIdentifier=new_cluster_identifier, ClusterIdentifier=new_cluster_identifier,
SnapshotIdentifier=original_snapshot_identifier, SnapshotIdentifier=original_snapshot_identifier,
@ -842,7 +920,7 @@ def test_create_cluster_from_snapshot():
new_cluster['NodeType'].should.equal('ds2.xlarge') new_cluster['NodeType'].should.equal('ds2.xlarge')
new_cluster['MasterUsername'].should.equal('username') new_cluster['MasterUsername'].should.equal('username')
new_cluster['Endpoint']['Port'].should.equal(1234) new_cluster['Endpoint']['Port'].should.equal(1234)
new_cluster['EnhancedVpcRouting'].should.equal(True)
@mock_redshift @mock_redshift
def test_create_cluster_from_snapshot_with_waiter(): def test_create_cluster_from_snapshot_with_waiter():
@ -857,6 +935,7 @@ def test_create_cluster_from_snapshot_with_waiter():
NodeType='ds2.xlarge', NodeType='ds2.xlarge',
MasterUsername='username', MasterUsername='username',
MasterUserPassword='password', MasterUserPassword='password',
EnhancedVpcRouting=True
) )
client.create_cluster_snapshot( client.create_cluster_snapshot(
SnapshotIdentifier=original_snapshot_identifier, SnapshotIdentifier=original_snapshot_identifier,
@ -883,6 +962,7 @@ def test_create_cluster_from_snapshot_with_waiter():
new_cluster = response['Clusters'][0] new_cluster = response['Clusters'][0]
new_cluster['NodeType'].should.equal('ds2.xlarge') new_cluster['NodeType'].should.equal('ds2.xlarge')
new_cluster['MasterUsername'].should.equal('username') new_cluster['MasterUsername'].should.equal('username')
new_cluster['EnhancedVpcRouting'].should.equal(True)
new_cluster['Endpoint']['Port'].should.equal(1234) new_cluster['Endpoint']['Port'].should.equal(1234)