* Support for CloudFormation update and delete of Kinesis Streams (#3212)
* Support for CloudFormation stack resource deletion via backend resource method delete_from_cloudformation_json() via parse_and_delete_resource(). * Correction to the inappropriate inclusion of EndingSequenceNumber in open shards. This attribute should only appear in closed shards. This regretfully prevents confirmation of consistent record counts after split/merge in unit tests. * Added parameters/decorator to CloudFormationModel method declarations to calm-down Pycharm. Co-authored-by: Joseph Weitekamp <jweite@amazon.com>
This commit is contained in:
parent
99736c3101
commit
da07adae52
5 changed files with 268 additions and 36 deletions
|
|
@ -10,6 +10,8 @@ from boto.kinesis.exceptions import ResourceNotFoundException, InvalidArgumentEx
|
|||
from moto import mock_kinesis, mock_kinesis_deprecated
|
||||
from moto.core import ACCOUNT_ID
|
||||
|
||||
import sure # noqa
|
||||
|
||||
|
||||
@mock_kinesis_deprecated
|
||||
def test_create_cluster():
|
||||
|
|
@ -601,9 +603,6 @@ def test_split_shard():
|
|||
stream = stream_response["StreamDescription"]
|
||||
shards = stream["Shards"]
|
||||
shards.should.have.length_of(2)
|
||||
sum(
|
||||
[shard["SequenceNumberRange"]["EndingSequenceNumber"] for shard in shards]
|
||||
).should.equal(99)
|
||||
|
||||
shard_range = shards[0]["HashKeyRange"]
|
||||
new_starting_hash = (
|
||||
|
|
@ -616,9 +615,6 @@ def test_split_shard():
|
|||
stream = stream_response["StreamDescription"]
|
||||
shards = stream["Shards"]
|
||||
shards.should.have.length_of(3)
|
||||
sum(
|
||||
[shard["SequenceNumberRange"]["EndingSequenceNumber"] for shard in shards]
|
||||
).should.equal(99)
|
||||
|
||||
shard_range = shards[2]["HashKeyRange"]
|
||||
new_starting_hash = (
|
||||
|
|
@ -631,9 +627,6 @@ def test_split_shard():
|
|||
stream = stream_response["StreamDescription"]
|
||||
shards = stream["Shards"]
|
||||
shards.should.have.length_of(4)
|
||||
sum(
|
||||
[shard["SequenceNumberRange"]["EndingSequenceNumber"] for shard in shards]
|
||||
).should.equal(99)
|
||||
|
||||
|
||||
@mock_kinesis_deprecated
|
||||
|
|
@ -662,9 +655,6 @@ def test_merge_shards():
|
|||
stream = stream_response["StreamDescription"]
|
||||
shards = stream["Shards"]
|
||||
shards.should.have.length_of(4)
|
||||
sum(
|
||||
[shard["SequenceNumberRange"]["EndingSequenceNumber"] for shard in shards]
|
||||
).should.equal(99)
|
||||
|
||||
conn.merge_shards(stream_name, "shardId-000000000000", "shardId-000000000001")
|
||||
|
||||
|
|
@ -672,17 +662,23 @@ def test_merge_shards():
|
|||
|
||||
stream = stream_response["StreamDescription"]
|
||||
shards = stream["Shards"]
|
||||
shards.should.have.length_of(3)
|
||||
sum(
|
||||
[shard["SequenceNumberRange"]["EndingSequenceNumber"] for shard in shards]
|
||||
).should.equal(99)
|
||||
active_shards = [
|
||||
shard
|
||||
for shard in shards
|
||||
if "EndingSequenceNumber" not in shard["SequenceNumberRange"]
|
||||
]
|
||||
active_shards.should.have.length_of(3)
|
||||
|
||||
conn.merge_shards(stream_name, "shardId-000000000002", "shardId-000000000000")
|
||||
|
||||
stream_response = conn.describe_stream(stream_name)
|
||||
|
||||
stream = stream_response["StreamDescription"]
|
||||
shards = stream["Shards"]
|
||||
shards.should.have.length_of(2)
|
||||
sum(
|
||||
[shard["SequenceNumberRange"]["EndingSequenceNumber"] for shard in shards]
|
||||
).should.equal(99)
|
||||
active_shards = [
|
||||
shard
|
||||
for shard in shards
|
||||
if "EndingSequenceNumber" not in shard["SequenceNumberRange"]
|
||||
]
|
||||
|
||||
active_shards.should.have.length_of(2)
|
||||
|
|
|
|||
144
tests/test_kinesis/test_kinesis_cloudformation.py
Normal file
144
tests/test_kinesis/test_kinesis_cloudformation.py
Normal file
|
|
@ -0,0 +1,144 @@
|
|||
import boto3
|
||||
import sure # noqa
|
||||
|
||||
from moto import mock_kinesis, mock_cloudformation
|
||||
|
||||
|
||||
@mock_cloudformation
|
||||
def test_kinesis_cloudformation_create_stream():
|
||||
cf_conn = boto3.client("cloudformation", region_name="us-east-1")
|
||||
|
||||
stack_name = "MyStack"
|
||||
|
||||
template = '{"Resources":{"MyStream":{"Type":"AWS::Kinesis::Stream"}}}'
|
||||
|
||||
cf_conn.create_stack(StackName=stack_name, TemplateBody=template)
|
||||
|
||||
provisioned_resource = cf_conn.list_stack_resources(StackName=stack_name)[
|
||||
"StackResourceSummaries"
|
||||
][0]
|
||||
provisioned_resource["LogicalResourceId"].should.equal("MyStream")
|
||||
len(provisioned_resource["PhysicalResourceId"]).should.be.greater_than(0)
|
||||
|
||||
|
||||
@mock_cloudformation
|
||||
@mock_kinesis
|
||||
def test_kinesis_cloudformation_get_attr():
|
||||
cf_conn = boto3.client("cloudformation", region_name="us-east-1")
|
||||
|
||||
stack_name = "MyStack"
|
||||
|
||||
template = """
|
||||
Resources:
|
||||
TheStream:
|
||||
Type: AWS::Kinesis::Stream
|
||||
Outputs:
|
||||
StreamName:
|
||||
Value: !Ref TheStream
|
||||
StreamArn:
|
||||
Value: !GetAtt TheStream.Arn
|
||||
""".strip()
|
||||
|
||||
cf_conn.create_stack(StackName=stack_name, TemplateBody=template)
|
||||
stack_description = cf_conn.describe_stacks(StackName=stack_name)["Stacks"][0]
|
||||
output_stream_name = [
|
||||
output["OutputValue"]
|
||||
for output in stack_description["Outputs"]
|
||||
if output["OutputKey"] == "StreamName"
|
||||
][0]
|
||||
output_stream_arn = [
|
||||
output["OutputValue"]
|
||||
for output in stack_description["Outputs"]
|
||||
if output["OutputKey"] == "StreamArn"
|
||||
][0]
|
||||
|
||||
kinesis_conn = boto3.client("kinesis", region_name="us-east-1")
|
||||
stream_description = kinesis_conn.describe_stream(StreamName=output_stream_name)[
|
||||
"StreamDescription"
|
||||
]
|
||||
output_stream_arn.should.equal(stream_description["StreamARN"])
|
||||
|
||||
|
||||
@mock_cloudformation
|
||||
@mock_kinesis
|
||||
def test_kinesis_cloudformation_update():
|
||||
cf_conn = boto3.client("cloudformation", region_name="us-east-1")
|
||||
|
||||
stack_name = "MyStack"
|
||||
|
||||
template = """
|
||||
Resources:
|
||||
TheStream:
|
||||
Type: AWS::Kinesis::Stream
|
||||
Properties:
|
||||
Name: MyStream
|
||||
ShardCount: 4
|
||||
""".strip()
|
||||
|
||||
cf_conn.create_stack(StackName=stack_name, TemplateBody=template)
|
||||
stack_description = cf_conn.describe_stacks(StackName=stack_name)["Stacks"][0]
|
||||
stack_description["StackName"].should.equal(stack_name)
|
||||
|
||||
kinesis_conn = boto3.client("kinesis", region_name="us-east-1")
|
||||
stream_description = kinesis_conn.describe_stream(StreamName="MyStream")[
|
||||
"StreamDescription"
|
||||
]
|
||||
shards_provisioned = len(
|
||||
[
|
||||
shard
|
||||
for shard in stream_description["Shards"]
|
||||
if "EndingSequenceNumber" not in shard["SequenceNumberRange"]
|
||||
]
|
||||
)
|
||||
shards_provisioned.should.equal(4)
|
||||
|
||||
template = """
|
||||
Resources:
|
||||
TheStream:
|
||||
Type: AWS::Kinesis::Stream
|
||||
Properties:
|
||||
ShardCount: 6
|
||||
""".strip()
|
||||
cf_conn.update_stack(StackName=stack_name, TemplateBody=template)
|
||||
|
||||
stream_description = kinesis_conn.describe_stream(StreamName="MyStream")[
|
||||
"StreamDescription"
|
||||
]
|
||||
shards_provisioned = len(
|
||||
[
|
||||
shard
|
||||
for shard in stream_description["Shards"]
|
||||
if "EndingSequenceNumber" not in shard["SequenceNumberRange"]
|
||||
]
|
||||
)
|
||||
shards_provisioned.should.equal(6)
|
||||
|
||||
|
||||
@mock_cloudformation
|
||||
@mock_kinesis
|
||||
def test_kinesis_cloudformation_delete():
|
||||
cf_conn = boto3.client("cloudformation", region_name="us-east-1")
|
||||
|
||||
stack_name = "MyStack"
|
||||
|
||||
template = """
|
||||
Resources:
|
||||
TheStream:
|
||||
Type: AWS::Kinesis::Stream
|
||||
Properties:
|
||||
Name: MyStream
|
||||
""".strip()
|
||||
|
||||
cf_conn.create_stack(StackName=stack_name, TemplateBody=template)
|
||||
stack_description = cf_conn.describe_stacks(StackName=stack_name)["Stacks"][0]
|
||||
stack_description["StackName"].should.equal(stack_name)
|
||||
|
||||
kinesis_conn = boto3.client("kinesis", region_name="us-east-1")
|
||||
stream_description = kinesis_conn.describe_stream(StreamName="MyStream")[
|
||||
"StreamDescription"
|
||||
]
|
||||
stream_description["StreamName"].should.equal("MyStream")
|
||||
|
||||
cf_conn.delete_stack(StackName=stack_name)
|
||||
streams = kinesis_conn.list_streams()["StreamNames"]
|
||||
len(streams).should.equal(0)
|
||||
Loading…
Add table
Add a link
Reference in a new issue