def test_sns_topic_fifo_without_suffix_fails( cfn_client, sns_client, cleanup_stacks, cleanup_changesets, is_change_set_created_and_available, is_stack_created, ): """topic name needs .fifo suffix to be valid""" stack_name = f"stack-{short_uid()}" change_set_name = f"change-set-{short_uid()}" topic_name = f"topic-{short_uid()}" template_rendered = jinja2.Template(load_template_raw("sns_topic_fifo_dedup.yaml")).render( sns_topic=topic_name ) response = cfn_client.create_change_set( StackName=stack_name, ChangeSetName=change_set_name, TemplateBody=template_rendered, ChangeSetType="CREATE", ) change_set_id = response["Id"] stack_id = response["StackId"] try: wait_until(is_change_set_created_and_available(change_set_id)) cfn_client.execute_change_set(ChangeSetName=change_set_id) wait_until(is_stack_created(stack_id)) stack = cfn_client.describe_stacks(StackName=stack_id)["Stacks"][0] assert stack.get("StackStatus") == "CREATE_FAILED" # TODO: might be different on AWS, check finally: cleanup_changesets([change_set_id]) cleanup_stacks([stack_id])
def test_execute_change_set(cfn_client, sns_client, is_change_set_finished, cleanup_changesets, cleanup_stacks): """check if executing a change set succeeds in creating/modifying the resources in changed""" stack_name = f"stack-{short_uid()}" change_set_name = f"change-set-{short_uid()}" response = cfn_client.create_change_set( StackName=stack_name, ChangeSetName=change_set_name, TemplateBody=load_template_raw("sns_topic_simple.yaml"), ChangeSetType="CREATE", ) change_set_id = response["Id"] stack_id = response["StackId"] assert change_set_id assert stack_id try: cfn_client.execute_change_set(ChangeSetName=change_set_id) wait_until(is_change_set_finished(change_set_id)) # check if stack resource was created topics = sns_client.list_topics() topic_arns = list(map(lambda x: x["TopicArn"], topics["Topics"])) assert any(("sns-topic-simple" in t) for t in topic_arns) finally: cleanup_changesets([change_set_id]) cleanup_stacks([stack_id])
def test_create_change_set_create_existing(cfn_client, is_stack_created, cleanup_changesets, cleanup_stacks): """tries to create an already existing stack""" stack_name = f"stack-{short_uid()}" change_set_name = f"change-set-{short_uid()}" response = cfn_client.create_change_set( StackName=stack_name, ChangeSetName=change_set_name, TemplateBody=load_template_raw("sns_topic_simple.yaml"), ChangeSetType="CREATE", ) change_set_id = response["Id"] stack_id = response["StackId"] assert change_set_id assert stack_id try: cfn_client.execute_change_set(ChangeSetName=change_set_id) wait_until(is_stack_created(stack_id)) with pytest.raises(Exception) as ex: change_set_name2 = f"change-set-{short_uid()}" cfn_client.create_change_set( StackName=stack_name, ChangeSetName=change_set_name2, TemplateBody=load_template_raw("sns_topic_simple.yaml"), ChangeSetType="CREATE", ) assert ex is not None finally: cleanup_changesets([change_set_id]) cleanup_stacks([stack_id])
def test_cfn_secretsmanager_gen_secret( cfn_client, secretsmanager_client, is_stack_created, cleanup_stacks, ): stack_name = f"stack-{short_uid()}" response = cfn_client.create_stack( StackName=stack_name, TemplateBody=TEMPLATE_GENERATE_SECRET, ) stack_id = response["StackId"] try: wait_until(is_stack_created(stack_id)) secret = secretsmanager_client.describe_secret(SecretId="/dev/db/pass") assert "/dev/db/pass" == secret["Name"] assert "secret:/dev/db/pass" in secret["ARN"] # assert that secret has ben generated and added to the result template JSON value = secretsmanager_client.get_secret_value(SecretId="/dev/db/pass") secret = value.get("SecretString") secret = json.loads(secret) assert "password" in secret assert len(secret["password"]) == 30 finally: cleanup_stacks([stack_id])
def test_bucket_autoname( cfn_client, cleanup_stacks, cleanup_changesets, is_change_set_created_and_available, is_stack_created, ): stack_name = f"STACK-{short_uid()}" change_set_name = f"change-set-{short_uid()}" response = cfn_client.create_change_set( StackName=stack_name, ChangeSetName=change_set_name, TemplateBody=load_template_raw("s3_bucket_autoname.yaml"), ChangeSetType="CREATE", ) change_set_id = response["Id"] stack_id = response["StackId"] try: wait_until(is_change_set_created_and_available(change_set_id)) cfn_client.execute_change_set(ChangeSetName=change_set_id) wait_until(is_stack_created(stack_id)) descr_response = cfn_client.describe_stacks(StackName=stack_id) output = descr_response["Stacks"][0]["Outputs"][0] assert output["OutputKey"] == "BucketNameOutput" assert stack_name.lower() in output["OutputValue"] finally: cleanup_changesets([change_set_id]) cleanup_stacks([stack_id])
def test_cfn_secretsmanager_gen_secret( cfn_client, secretsmanager_client, is_stack_created, cleanup_stacks, ): stack_name = f"stack-{short_uid()}" response = cfn_client.create_stack( StackName=stack_name, TemplateBody=TEMPLATE_GENERATE_SECRET, ) stack_id = response["StackId"] try: wait_until(is_stack_created(stack_id)) secret = secretsmanager_client.describe_secret(SecretId="/dev/db/pass") assert "/dev/db/pass" == secret["Name"] assert "secret:/dev/db/pass" in secret["ARN"] # assert that secret has ben generated and added to the result template JSON value = secretsmanager_client.get_secret_value(SecretId="/dev/db/pass") secret = value.get("SecretString") secret = json.loads(secret) assert "password" in secret assert len(secret["password"]) == 30 # assert that the Ref properly returns the secret ARN result = cfn_client.describe_stacks(StackName=stack_name)["Stacks"][0] assert len(result["Outputs"]) == 1 assert result["Outputs"][0]["OutputKey"] == "SecretARN" assert re.match(r".*%s-[a-zA-Z0-9]+" % SECRET_NAME, result["Outputs"][0]["OutputValue"]) finally: cleanup_stacks([stack_id])
def test_resolve_ssm_withversion( ssm_client, cfn_client, is_change_set_created_and_available, is_stack_created, cleanup_changesets, cleanup_stacks, create_parameter, ): stack_name = f"stack-{short_uid()}" change_set_name = f"change-set-{short_uid()}" parameter_key = f"param-key-{short_uid()}" parameter_value_v0 = f"param-value-{short_uid()}" parameter_value_v1 = f"param-value-{short_uid()}" parameter_value_v2 = f"param-value-{short_uid()}" create_parameter(Name=parameter_key, Type="String", Value=parameter_value_v0) v1 = ssm_client.put_parameter(Name=parameter_key, Overwrite=True, Type="String", Value=parameter_value_v1) ssm_client.put_parameter(Name=parameter_key, Overwrite=True, Type="String", Value=parameter_value_v2) template_rendered = jinja2.Template( load_template_raw("resolve_ssm_withversion.yaml")).render( parameter_key=parameter_key, parameter_version=str(v1["Version"])) response = cfn_client.create_change_set( StackName=stack_name, ChangeSetName=change_set_name, TemplateBody=template_rendered, ChangeSetType="CREATE", ) change_set_id = response["Id"] stack_id = response["StackId"] try: wait_until(is_change_set_created_and_available(change_set_id)) cfn_client.execute_change_set(ChangeSetName=change_set_id) wait_until(is_stack_created(stack_id)) describe_result = cfn_client.describe_stacks( StackName=stack_id)["Stacks"][0] assert describe_result["StackStatus"] == "CREATE_COMPLETE" topic_name = [ o["OutputValue"] for o in describe_result["Outputs"] if o["OutputKey"] == "TopicName" ][0] assert topic_name == parameter_value_v1 finally: cleanup_changesets([change_set_id]) cleanup_stacks([stack_id])
def test_intrinsic_functions( cfn_client, s3_client, cleanup_stacks, cleanup_changesets, is_change_set_created_and_available, is_stack_created, intrinsic_fn, parameter_1, parameter_2, expected_bucket_created, ): stack_name = f"stack-{short_uid()}" change_set_name = f"change-set-{short_uid()}" bucket_name = f"ls-bucket-{short_uid()}" template_rendered = jinja2.Template( load_template_raw("cfn_intrinsic_functions.yaml")).render( bucket_name=bucket_name, intrinsic_fn=intrinsic_fn, ) response = cfn_client.create_change_set( StackName=stack_name, ChangeSetName=change_set_name, TemplateBody=template_rendered, ChangeSetType="CREATE", Parameters=[ { "ParameterKey": "Param1", "ParameterValue": parameter_1 }, { "ParameterKey": "Param2", "ParameterValue": parameter_2 }, ], ) change_set_id = response["Id"] stack_id = response["StackId"] try: wait_until(is_change_set_created_and_available(change_set_id)) cfn_client.execute_change_set(ChangeSetName=change_set_id) wait_until(is_stack_created(stack_id)) buckets = s3_client.list_buckets() bucket_names = [b["Name"] for b in buckets["Buckets"]] assert (bucket_name in bucket_names) == expected_bucket_created finally: cleanup_changesets([change_set_id]) cleanup_stacks([stack_id])
def test_create_change_set_without_parameters( cfn_client, sns_client, cleanup_stacks, cleanup_changesets, is_change_set_created_and_available): stack_name = f"stack-{short_uid()}" change_set_name = f"change-set-{short_uid()}" response = cfn_client.create_change_set( StackName=stack_name, ChangeSetName=change_set_name, TemplateBody=load_template_raw("sns_topic_simple.yaml"), ChangeSetType="CREATE", ) change_set_id = response["Id"] stack_id = response["StackId"] assert change_set_id assert stack_id try: # make sure the change set wasn't executed (which would create a topic) topics = sns_client.list_topics() topic_arns = list(map(lambda x: x["TopicArn"], topics["Topics"])) assert not any("sns-topic-simple" in arn for arn in topic_arns) # stack is initially in REVIEW_IN_PROGRESS state. only after executing the change_set will it change its status stack_response = cfn_client.describe_stacks(StackName=stack_id) assert stack_response["Stacks"][0][ "StackStatus"] == "REVIEW_IN_PROGRESS" # Change set can now either be already created/available or it is pending/unavailable wait_until(is_change_set_created_and_available(change_set_id), 2, 10, strategy="exponential") describe_response = cfn_client.describe_change_set( ChangeSetName=change_set_id) assert describe_response["ChangeSetName"] == change_set_name assert describe_response["ChangeSetId"] == change_set_id assert describe_response["StackId"] == stack_id assert describe_response["StackName"] == stack_name assert describe_response["ExecutionStatus"] == "AVAILABLE" assert describe_response["Status"] == "CREATE_COMPLETE" changes = describe_response["Changes"] assert len(changes) == 1 assert changes[0]["Type"] == "Resource" assert changes[0]["ResourceChange"]["Action"] == "Add" assert changes[0]["ResourceChange"][ "ResourceType"] == "AWS::SNS::Topic" assert changes[0]["ResourceChange"]["LogicalResourceId"] == "topic123" finally: cleanup_stacks([stack_id]) cleanup_changesets([change_set_id])
def test_multiregion_nested(region_name, statemachine_definition): client1 = aws_stack.create_external_boto_client("stepfunctions", region_name=region_name) # create state machine child_machine_name = f"sf-child-{short_uid()}" role = aws_stack.role_arn("sfn_role") child_machine_result = client1.create_state_machine( name=child_machine_name, definition=json.dumps(TEST_STATE_MACHINE), roleArn=role) child_machine_arn = child_machine_result["stateMachineArn"] # create parent state machine name = f"sf-parent-{short_uid()}" role = aws_stack.role_arn("sfn_role") result = client1.create_state_machine( name=name, definition=json.dumps(statemachine_definition).replace( "__machine_arn__", child_machine_arn), roleArn=role, ) machine_arn = result["stateMachineArn"] try: # list state machine result = client1.list_state_machines()["stateMachines"] assert len(result) > 0 assert len([sm for sm in result if sm["name"] == name]) == 1 assert len([sm for sm in result if sm["name"] == child_machine_name]) == 1 # start state machine execution result = client1.start_execution(stateMachineArn=machine_arn) execution = client1.describe_execution( executionArn=result["executionArn"]) assert execution["stateMachineArn"] == machine_arn assert execution["status"] in ["RUNNING", "SUCCEEDED"] def assert_success(): return (client1.describe_execution( executionArn=result["executionArn"])["status"] == "SUCCEEDED") wait_until(assert_success) result = client1.describe_state_machine_for_execution( executionArn=result["executionArn"]) assert result["stateMachineArn"] == machine_arn finally: client1.delete_state_machine(stateMachineArn=machine_arn) client1.delete_state_machine(stateMachineArn=child_machine_arn)
def test_logstream( cfn_client, logs_client, cleanup_stacks, cleanup_changesets, is_change_set_created_and_available, is_stack_created, ): stack_name = f"stack-{short_uid()}" change_set_name = f"change-set-{short_uid()}" response = cfn_client.create_change_set( StackName=stack_name, ChangeSetName=change_set_name, TemplateBody=load_template_raw("logs_group_and_stream.yaml"), ChangeSetType="CREATE", ) change_set_id = response["Id"] stack_id = response["StackId"] try: wait_until(is_change_set_created_and_available(change_set_id)) cfn_client.execute_change_set(ChangeSetName=change_set_id) wait_until(is_stack_created(stack_id)) assert (cfn_client.describe_stacks(StackName=stack_id)["Stacks"][0] ["StackStatus"] == "CREATE_COMPLETE") descr_response = cfn_client.describe_stacks(StackName=stack_id) outputs = { o["OutputKey"]: o["OutputValue"] for o in descr_response["Stacks"][0]["Outputs"] } group_name = outputs["LogGroupNameOutput"] stream_name = outputs["LogStreamNameOutput"] assert group_name assert stream_name streams = logs_client.describe_log_streams( logGroupName=group_name, logStreamNamePrefix=stream_name)["logStreams"] assert len(streams) == 1 assert streams[0]["logStreamName"] == stream_name assert re.match( r"arn:(aws|aws-cn|aws-iso|aws-iso-b|aws-us-gov):logs:.+:.+:log-group:.+:log-stream:.+", streams[0]["arn"], ) assert testutil.response_arn_matches_partition(cfn_client, streams[0]["arn"]) finally: cleanup_changesets([change_set_id]) cleanup_stacks([stack_id])
def test_url_output( cfn_client, apigateway_client, is_change_set_created_and_available, is_stack_created, cleanup_changesets, cleanup_stacks, tmp_http_server, ): test_port, invocations, proxy = tmp_http_server integration_uri = f"http://localhost:{test_port}/{{proxy}}" api_name = f"rest-api-{short_uid()}" stack_name = f"stack-{short_uid()}" change_set_name = f"change-set-{short_uid()}" template_rendered = jinja2.Template( load_template_raw("apigateway-url-output.yaml")).render( api_name=api_name, integration_uri=integration_uri) response = cfn_client.create_change_set( StackName=stack_name, ChangeSetName=change_set_name, TemplateBody=template_rendered, ChangeSetType="CREATE", ) change_set_id = response["Id"] stack_id = response["StackId"] try: wait_until(is_change_set_created_and_available(change_set_id)) cfn_client.execute_change_set(ChangeSetName=change_set_id) wait_until(is_stack_created(stack_id)) describe_response = cfn_client.describe_stacks(StackName=stack_id) outputs = describe_response["Stacks"][0]["Outputs"] assert len(outputs) == 2 api_id = [ o["OutputValue"] for o in outputs if o["OutputKey"] == "ApiV1IdOutput" ][0] api_url = [ o["OutputValue"] for o in outputs if o["OutputKey"] == "ApiV1UrlOutput" ][0] assert api_id assert api_url assert api_id in api_url assert f"https://{api_id}.execute-api.{constants.LOCALHOST_HOSTNAME}:4566" in api_url finally: cleanup_changesets([change_set_id]) cleanup_stacks([stack_id])
def test_bucketpolicy( cfn_client, s3_client, cleanup_stacks, cleanup_changesets, is_change_set_created_and_available, is_stack_created, ): stack_name = f"stack-{short_uid()}" change_set_name = f"change-set-{short_uid()}" bucket_name = f"ls-bucket-{short_uid()}" template_rendered = jinja2.Template(load_template_raw("s3_bucketpolicy.yaml")).render( bucket_name=bucket_name, include_policy=True, ) response = cfn_client.create_change_set( StackName=stack_name, ChangeSetName=change_set_name, TemplateBody=template_rendered, ChangeSetType="CREATE", ) change_set_id = response["Id"] stack_id = response["StackId"] try: wait_until(is_change_set_created_and_available(change_set_id)) cfn_client.execute_change_set(ChangeSetName=change_set_id) wait_until(is_stack_created(stack_id)) bucket_policy = s3_client.get_bucket_policy(Bucket=bucket_name)["Policy"] assert bucket_policy nopolicy_template = jinja2.Template(load_template_raw("s3_bucketpolicy.yaml")).render( bucket_name=bucket_name, include_policy=False, ) nopolicy_changeset_name = f"change-set-{short_uid()}" response = cfn_client.create_change_set( StackName=stack_name, ChangeSetName=nopolicy_changeset_name, TemplateBody=nopolicy_template, ChangeSetType="UPDATE", ) change_set_id = response["Id"] wait_until(is_change_set_created_and_available(change_set_id)) cfn_client.execute_change_set(ChangeSetName=change_set_id) wait_until( is_stack_created(stack_id) ) # TODO: fix cloudformation update status when using changesets with pytest.raises(Exception) as err: s3_client.get_bucket_policy(Bucket=bucket_name).get("Policy") assert err.value.response["Error"]["Code"] == "NoSuchBucketPolicy" finally: # pass cleanup_changesets([change_set_id]) cleanup_stacks([stack_id])
def test_eventbus_policy_statement( cfn_client, events_client, cleanup_stacks, cleanup_changesets, is_change_set_created_and_available, is_stack_created, ): stack_name = f"stack-{short_uid()}" change_set_name = f"change-set-{short_uid()}" event_bus_name = f"event-bus-{short_uid()}" statement_id = f"statement-{short_uid()}" template_rendered = jinja2.Template( load_template_raw("eventbridge_policy_statement.yaml")).render( event_bus_name=event_bus_name, statement_id=statement_id) response = cfn_client.create_change_set( StackName=stack_name, ChangeSetName=change_set_name, TemplateBody=template_rendered, ChangeSetType="CREATE", ) change_set_id = response["Id"] stack_id = response["StackId"] try: wait_until(is_change_set_created_and_available(change_set_id)) cfn_client.execute_change_set(ChangeSetName=change_set_id) wait_until(is_stack_created(stack_id)) assert (cfn_client.describe_stacks(StackName=stack_id)["Stacks"][0] ["StackStatus"] == "CREATE_COMPLETE") describe_response = events_client.describe_event_bus( Name=event_bus_name) policy = json.loads(describe_response["Policy"]) assert policy["Version"] == "2012-10-17" assert len(policy["Statement"]) == 1 statement = policy["Statement"][0] assert statement["Sid"] == statement_id assert statement["Action"] == "events:PutEvents" assert statement["Principal"] == "*" assert statement["Effect"] == "Allow" assert event_bus_name in statement["Resource"] finally: cleanup_changesets([change_set_id]) cleanup_stacks([stack_id])
def _wait_until_ready(function_name: str, qualifier: str = None): def _is_not_pending(): kwargs = {} if qualifier: kwargs["Qualifier"] = qualifier try: result = (lambda_client.get_function( FunctionName=function_name)["Configuration"]["State"] != "Pending") LOG.debug(f"lambda state result: {result=}") return result except Exception as e: LOG.error(e) raise wait_until(_is_not_pending)
def test_resolve_secretsmanager( secretsmanager_client, cfn_client, is_change_set_created_and_available, is_stack_created, create_secret, create_parameter, cleanup_changesets, cleanup_stacks, template_name, ): stack_name = f"stack-{short_uid()}" change_set_name = f"change-set-{short_uid()}" parameter_key = f"param-key-{short_uid()}" parameter_value = f"param-value-{short_uid()}" create_secret(Name=parameter_key, SecretString=parameter_value) template_rendered = jinja2.Template( load_template_raw(template_name)).render(parameter_key=parameter_key, ) response = cfn_client.create_change_set( StackName=stack_name, ChangeSetName=change_set_name, TemplateBody=template_rendered, ChangeSetType="CREATE", ) change_set_id = response["Id"] stack_id = response["StackId"] try: wait_until(is_change_set_created_and_available(change_set_id)) cfn_client.execute_change_set(ChangeSetName=change_set_id) wait_until(is_stack_created(stack_id)) describe_result = cfn_client.describe_stacks( StackName=stack_id)["Stacks"][0] assert describe_result["StackStatus"] == "CREATE_COMPLETE" topic_name = [ o["OutputValue"] for o in describe_result["Outputs"] if o["OutputKey"] == "TopicName" ][0] assert topic_name == parameter_value finally: cleanup_changesets([change_set_id]) cleanup_stacks([stack_id])
def _deploy( *, is_update: Optional[bool] = False, stack_name: Optional[str] = None, template: Optional[str] = None, template_file_name: Optional[str] = None, template_mapping: Optional[Dict[str, any]] = None, parameters: Optional[Dict[str, str]] = None, ) -> DeployResult: if is_update: assert stack_name else: stack_name = f"stack-{short_uid()}" change_set_name = f"change-set-{short_uid()}" if template_file_name is not None and os.path.exists( template_path(template_file_name)): template = load_file(template_path(template_file_name)) template_rendered = render_template(template, **(template_mapping or {})) response = cfn_client.create_change_set( StackName=stack_name, ChangeSetName=change_set_name, TemplateBody=template_rendered, ChangeSetType=("UPDATE" if is_update else "CREATE"), Parameters=[{ "ParameterKey": k, "ParameterValue": v, } for (k, v) in (parameters or {}).items()], ) change_set_id = response["Id"] stack_id = response["StackId"] assert wait_until(is_change_set_created_and_available(change_set_id)) cfn_client.execute_change_set(ChangeSetName=change_set_id) assert wait_until(is_change_set_finished(change_set_id)) outputs = cfn_client.describe_stacks( StackName=stack_id)["Stacks"][0].get("Outputs", []) mapped_outputs = {o["OutputKey"]: o["OutputValue"] for o in outputs} state.append({"stack_id": stack_id, "change_set_id": change_set_id}) return DeployResult(change_set_id, stack_id, stack_name, change_set_name, mapped_outputs)
def _create_function(): resp = testutil.create_lambda_function(**kwargs) lambda_arns.append(resp["CreateFunctionResponse"]["FunctionArn"]) def _is_not_pending(): try: result = (lambda_client.get_function( FunctionName=kwargs["func_name"])["Configuration"] ["State"] != "Pending") LOG.debug(f"lambda state result: {result=}") return result except Exception as e: LOG.error(e) raise wait_until(_is_not_pending) return resp
def test_update_lambda_inline_code(cfn_client, lambda_client, is_stack_created, is_stack_updated, cleanup_stacks): stack_name = f"stack-{short_uid()}" function_name = f"test-fn-{short_uid()}" try: template_1 = jinja2.Template( load_template_raw("lambda_inline_code.yaml")).render( lambda_return_value="hello world", arch="x86_64", function_name=function_name, ) response = cfn_client.create_stack(StackName=stack_name, TemplateBody=template_1, Capabilities=["CAPABILITY_IAM"]) stack_id = response["StackId"] assert stack_id wait_until(is_stack_created(stack_id)) rs = lambda_client.get_function(FunctionName=function_name) assert function_name == rs["Configuration"]["FunctionName"] assert "x86_64" in rs["Configuration"]["Architectures"] result = lambda_client.invoke(FunctionName=function_name) result = to_str(result["Payload"].read()) assert result.strip('" \n') == "hello world" template_2 = jinja2.Template( load_template_raw("lambda_inline_code.yaml")).render( lambda_return_value="hello globe", arch="arm64", function_name=function_name) cfn_client.update_stack(StackName=stack_name, TemplateBody=template_2, Capabilities=["CAPABILITY_IAM"]) wait_until(is_stack_updated(stack_id)) rs = lambda_client.get_function(FunctionName=function_name) assert function_name == rs["Configuration"]["FunctionName"] assert "arm64" in rs["Configuration"]["Architectures"] result = lambda_client.invoke(FunctionName=function_name) result = to_str(result["Payload"].read()) assert result.strip('" \n') == "hello globe" finally: # cleanup cleanup_stacks([stack_name])
def test_apigateway_invoke_with_path(cfn_client, deploy_cfn_template, stepfunctions_client): deploy_result = deploy_cfn_template(template_file_name="sfn_apigateway_two_integrations.yaml") state_machine_arn = deploy_result.outputs["statemachineOutput"] execution_arn = stepfunctions_client.start_execution(stateMachineArn=state_machine_arn)[ "executionArn" ] def _sfn_finished_running(): return ( stepfunctions_client.describe_execution(executionArn=execution_arn)["status"] != "RUNNING" ) wait_until(_sfn_finished_running) execution_result = stepfunctions_client.describe_execution(executionArn=execution_arn) assert execution_result["status"] == "SUCCEEDED" assert "hello_with_path from stepfunctions" in execution_result["output"]
def test_list_stack_resources_for_removed_resource(cfn_client, is_stack_created, is_change_set_finished): event_bus_name = f"bus-{short_uid()}" template = jinja2.Template( load_template_raw("eventbridge_policy.yaml")).render( event_bus_name=event_bus_name) stack_name = f"stack-{short_uid()}" response = cfn_client.create_stack(StackName=stack_name, TemplateBody=template) stack_id = response["StackId"] assert stack_id wait_until(is_stack_created(stack_id)) # get list of stack resources resources = cfn_client.list_stack_resources( StackName=stack_name)["StackResourceSummaries"] resources_before = len(resources) assert resources_before == 3 statuses = set([res["ResourceStatus"] for res in resources]) assert statuses == {"CREATE_COMPLETE", "UPDATE_COMPLETE"} # remove one resource from the template, then update stack (via change set) template_dict = yaml.load(template) template_dict["Resources"].pop("eventPolicy2") template2 = yaml.dump(template_dict) response = cfn_client.create_change_set(StackName=stack_name, ChangeSetName="cs1", TemplateBody=template2) change_set_id = response["Id"] cfn_client.execute_change_set(ChangeSetName=change_set_id) wait_until(is_change_set_finished(change_set_id)) # get list of stack resources, again - make sure that deleted resource is not contained in result resources = cfn_client.list_stack_resources( StackName=stack_name)["StackResourceSummaries"] assert len(resources) == resources_before - 1 statuses = set([res["ResourceStatus"] for res in resources]) assert statuses == {"CREATE_COMPLETE", "UPDATE_COMPLETE"}
def _destroy_stack(): cfn_client.delete_stack(StackName=stack_id) def _await_stack_delete(): return (cfn_client.describe_stacks(StackName=stack_id) ["Stacks"][0]["StackStatus"] == "DELETE_COMPLETE") assert wait_until(_await_stack_delete, _max_wait=60) time.sleep( 2 ) # TODO: fix in localstack. stack should only be in DELETE_COMPLETE state after all resources have been deleted
def test_statemachine_definitionsubstitution( cfn_client, lambda_client, cleanup_stacks, cleanup_changesets, is_change_set_created_and_available, is_stack_created, stepfunctions_client, s3_client, ): stack_name = f"stack-{short_uid()}" change_set_name = f"change-set-{short_uid()}" response = cfn_client.create_change_set( StackName=stack_name, ChangeSetName=change_set_name, TemplateBody=load_template_raw("stepfunctions_statemachine_substitutions.yaml"), ChangeSetType="CREATE", Capabilities=["CAPABILITY_IAM"], ) change_set_id = response["Id"] stack_id = response["StackId"] try: wait_until(is_change_set_created_and_available(change_set_id)) cfn_client.execute_change_set(ChangeSetName=change_set_id) wait_until(is_stack_created(stack_id)) stack_result = cfn_client.describe_stacks(StackName=stack_id) assert stack_result["Stacks"][0]["StackStatus"] == "CREATE_COMPLETE" outputs = stack_result["Stacks"][0]["Outputs"] assert len(outputs) == 1 statemachine_arn = outputs[0]["OutputValue"] # execute statemachine ex_result = stepfunctions_client.start_execution(stateMachineArn=statemachine_arn) def _is_executed(): return ( stepfunctions_client.describe_execution(executionArn=ex_result["executionArn"])[ "status" ] != "RUNNING" ) wait_until(_is_executed) execution_desc = stepfunctions_client.describe_execution( executionArn=ex_result["executionArn"] ) assert execution_desc["status"] == "SUCCEEDED" # sync execution is currently not supported since botocore adds a "sync-" prefix # ex_result = stepfunctions_client.start_sync_execution(stateMachineArn=statemachine_arn) assert "hello from statemachine" in execution_desc["output"] finally: cleanup_changesets([change_set_id]) cleanup_stacks([stack_id])
def test_apigateway_invoke_localhost_with_path( cfn_client, deploy_cfn_template, stepfunctions_client ): """tests the same as above but with the "generic" localhost version of invoking the apigateway""" deploy_result = deploy_cfn_template(template_file_name="sfn_apigateway_two_integrations.yaml") state_machine_arn = deploy_result.outputs["statemachineOutput"] api_url = deploy_result.outputs["LsApiEndpointA06D37E8"] # instead of changing the template, we're just mapping the endpoint here to the more generic path-based version state_def = stepfunctions_client.describe_state_machine(stateMachineArn=state_machine_arn)[ "definition" ] parsed = urllib.parse.urlparse(api_url) api_id = parsed.hostname.split(".")[0] state = json.loads(state_def) stage = state["States"]["LsCallApi"]["Parameters"]["Stage"] state["States"]["LsCallApi"]["Parameters"]["ApiEndpoint"] = "localhost:4566" state["States"]["LsCallApi"]["Parameters"][ "Stage" ] = f"restapis/{api_id}/{stage}/{PATH_USER_REQUEST}" stepfunctions_client.update_state_machine( stateMachineArn=state_machine_arn, definition=json.dumps(state) ) execution_arn = stepfunctions_client.start_execution(stateMachineArn=state_machine_arn)[ "executionArn" ] def _sfn_finished_running(): return ( stepfunctions_client.describe_execution(executionArn=execution_arn)["status"] != "RUNNING" ) wait_until(_sfn_finished_running) execution_result = stepfunctions_client.describe_execution(executionArn=execution_arn) assert execution_result["status"] == "SUCCEEDED" assert "hello_with_path from stepfunctions" in execution_result["output"]
def test_sns_subscription( cfn_client, sns_client, cleanup_stacks, cleanup_changesets, is_change_set_created_and_available, is_stack_created, ): stack_name = f"stack-{short_uid()}" change_set_name = f"change-set-{short_uid()}" topic_name = f"topic-{short_uid()}" queue_name = f"topic-{short_uid()}" template_rendered = jinja2.Template(load_template_raw("sns_topic_subscription.yaml")).render( topic_name=topic_name, queue_name=queue_name ) response = cfn_client.create_change_set( StackName=stack_name, ChangeSetName=change_set_name, TemplateBody=template_rendered, ChangeSetType="CREATE", ) change_set_id = response["Id"] stack_id = response["StackId"] try: wait_until(is_change_set_created_and_available(change_set_id)) cfn_client.execute_change_set(ChangeSetName=change_set_id) wait_until(is_stack_created(stack_id)) outputs = cfn_client.describe_stacks(StackName=stack_id)["Stacks"][0]["Outputs"] assert len(outputs) == 1 and outputs[0]["OutputKey"] == "TopicArnOutput" topic_arn = outputs[0]["OutputValue"] assert topic_arn is not None subscriptions = sns_client.list_subscriptions_by_topic(TopicArn=topic_arn) assert len(subscriptions["Subscriptions"]) > 0 finally: cleanup_changesets([change_set_id]) cleanup_stacks([stack_id])
def test_kms_key_disabled( cfn_client, sqs_client, kms_client, cleanup_stacks, cleanup_changesets, is_change_set_created_and_available, is_stack_created, ): stack_name = f"stack-{short_uid()}" change_set_name = f"change-set-{short_uid()}" template_rendered = load_template_raw("kms_key_disabled.yaml") response = cfn_client.create_change_set( StackName=stack_name, ChangeSetName=change_set_name, TemplateBody=template_rendered, ChangeSetType="CREATE", ) change_set_id = response["Id"] stack_id = response["StackId"] try: wait_until(is_change_set_created_and_available(change_set_id)) cfn_client.execute_change_set(ChangeSetName=change_set_id) wait_until(is_stack_created(stack_id)) outputs = cfn_client.describe_stacks( StackName=stack_id)["Stacks"][0]["Outputs"] assert len(outputs) == 1 key_id = outputs[0]["OutputValue"] assert key_id my_key = kms_client.describe_key(KeyId=key_id) assert not my_key["KeyMetadata"]["Enabled"] finally: cleanup_changesets([change_set_id]) cleanup_stacks([stack_id])
def test_create_change_set_update_without_parameters( cfn_client, sns_client, cleanup_stacks, cleanup_changesets, is_change_set_created_and_available, is_change_set_finished, ): """after creating a stack via a CREATE change set we send an UPDATE change set changing the SNS topic name""" stack_name = f"stack-{short_uid()}" change_set_name = f"change-set-{short_uid()}" change_set_name2 = f"change-set-{short_uid()}" response = cfn_client.create_change_set( StackName=stack_name, ChangeSetName=change_set_name, TemplateBody=load_template_raw("sns_topic_simple.yaml"), ChangeSetType="CREATE", ) change_set_id = response["Id"] stack_id = response["StackId"] assert change_set_id assert stack_id try: # Change set can now either be already created/available or it is pending/unavailable wait_until(is_change_set_created_and_available(change_set_id)) cfn_client.execute_change_set(ChangeSetName=change_set_id) wait_until(is_change_set_finished(change_set_id)) template = load_template_raw("sns_topic_simple.yaml") update_response = cfn_client.create_change_set( StackName=stack_name, ChangeSetName=change_set_name2, TemplateBody=template.replace("sns-topic-simple", "sns-topic-simple-2"), ChangeSetType="UPDATE", ) wait_until(is_change_set_created_and_available(update_response["Id"])) describe_response = cfn_client.describe_change_set( ChangeSetName=update_response["Id"]) changes = describe_response["Changes"] assert len(changes) == 1 assert changes[0]["Type"] == "Resource" change = changes[0]["ResourceChange"] assert change["Action"] == "Modify" assert change["ResourceType"] == "AWS::SNS::Topic" assert change["LogicalResourceId"] == "topic123" assert "sns-topic-simple" in change["PhysicalResourceId"] assert change["Replacement"] == "True" assert "Properties" in change["Scope"] assert len(change["Details"]) == 1 assert change["Details"][0]["Target"]["Name"] == "TopicName" assert change["Details"][0]["Target"]["RequiresRecreation"] == "Always" finally: cleanup_changesets(changesets=[change_set_id]) cleanup_stacks(stacks=[stack_id])
def test_create_stack_with_ssm_parameters(cfn_client, ssm_client, sns_client, cleanup_stacks, is_stack_created): stack_name = f"stack-{short_uid()}" parameter_name = f"ls-param-{short_uid()}" parameter_value = f"ls-param-value-{short_uid()}" parameter_logical_id = "parameter123" ssm_client.put_parameter(Name=parameter_name, Value=parameter_value, Type="String") template = load_template_raw("dynamicparameter_ssm_string.yaml") template_rendered = jinja2.Template(template).render( parameter_name=parameter_name) response = cfn_client.create_stack( StackName=stack_name, TemplateBody=template_rendered, ) stack_id = response["StackId"] assert stack_id try: wait_until(is_stack_created(stack_id)) created_stack = cfn_client.describe_stacks( StackName=stack_name)["Stacks"][0] assert created_stack is not None assert created_stack["Parameters"][0][ "ParameterKey"] == parameter_logical_id assert created_stack["Parameters"][0][ "ParameterValue"] == parameter_name assert created_stack["Parameters"][0][ "ResolvedValue"] == parameter_value topics = sns_client.list_topics() topic_arns = [t["TopicArn"] for t in topics["Topics"]] assert any(parameter_value in t for t in topic_arns) finally: cleanup_stacks([stack_id])
def test_lambda_autogenerated_name( cfn_client, lambda_client, cleanup_stacks, cleanup_changesets, is_change_set_created_and_available, is_stack_created, ): stack_name = f"stack-{short_uid()}" change_set_name = f"change-set-{short_uid()}" lambda_functional_id = f"MyFn{short_uid()}" template_rendered = jinja2.Template( load_template_raw("cfn_lambda_noname.yaml")).render( lambda_functional_id=lambda_functional_id) response = cfn_client.create_change_set( StackName=stack_name, ChangeSetName=change_set_name, TemplateBody=template_rendered, ChangeSetType="CREATE", ) change_set_id = response["Id"] stack_id = response["StackId"] try: wait_until(is_change_set_created_and_available(change_set_id)) cfn_client.execute_change_set(ChangeSetName=change_set_id) wait_until(is_stack_created(stack_id)) outputs = cfn_client.describe_stacks( StackName=stack_id)["Stacks"][0]["Outputs"] assert len(outputs) == 1 assert lambda_functional_id in outputs[0]["OutputValue"] finally: cleanup_changesets([change_set_id]) cleanup_stacks([stack_id])
def test_sns_topic_fifo_with_deduplication( cfn_client, sns_client, cleanup_stacks, cleanup_changesets, is_change_set_created_and_available, is_stack_created, ): stack_name = f"stack-{short_uid()}" change_set_name = f"change-set-{short_uid()}" topic_name = f"topic-{short_uid()}.fifo" template_rendered = jinja2.Template(load_template_raw("sns_topic_fifo_dedup.yaml")).render( sns_topic=topic_name ) response = cfn_client.create_change_set( StackName=stack_name, ChangeSetName=change_set_name, TemplateBody=template_rendered, ChangeSetType="CREATE", ) change_set_id = response["Id"] stack_id = response["StackId"] try: wait_until(is_change_set_created_and_available(change_set_id)) cfn_client.execute_change_set(ChangeSetName=change_set_id) wait_until(is_stack_created(stack_id)) topics = sns_client.list_topics()["Topics"] topic_arns = [t["TopicArn"] for t in topics] assert len([t for t in topic_arns if topic_name in t]) == 1 finally: cleanup_changesets([change_set_id]) cleanup_stacks([stack_id])