def test_stack_time_attributes(cfn_client, deploy_cfn_template): api_name = f"test_{short_uid()}" template_path = os.path.join(os.path.dirname(__file__), "../templates/simple_api.yaml") deployed = deploy_cfn_template( template_path=template_path, parameters={"ApiName": api_name}, ) stack_name = deployed.stack_name assert "CreationTime" in cfn_client.describe_stacks( StackName=stack_name)["Stacks"][0] api_name = f"test_{short_uid()}" stack_id = deployed.stack_id cfn_client.update_stack( StackName=stack_name, TemplateBody=load_template_file(template_path), Parameters=[{ "ParameterKey": "ApiName", "ParameterValue": api_name }], ) def wait_stack_done(): return cfn_client.describe_stacks( StackName=stack_id)["Stacks"][0]["StackStatus"] in [ "CREATE_COMPLETE", "UPDATE_COMPLETE", ] assert wait_until(wait_stack_done) assert "LastUpdatedTime" in cfn_client.describe_stacks( StackName=stack_name)["Stacks"][0] cfn_client.delete_stack(StackName=stack_name, ) assert "DeletionTime" in cfn_client.describe_stacks( StackName=stack_name)["Stacks"][0]
def load_template_raw(file_name: str): return load_template_file( os.path.join(os.path.dirname(__file__), "../templates", file_name))
def _deploy( *, is_update: Optional[bool] = False, stack_name: Optional[str] = None, change_set_name: Optional[str] = None, template: Optional[str] = None, template_path: Optional[str | os.PathLike] = None, template_mapping: Optional[Dict[str, any]] = None, parameters: Optional[Dict[str, str]] = None, ) -> DeployResult: if is_update: assert stack_name stack_name = stack_name or f"stack-{short_uid()}" change_set_name = change_set_name or f"change-set-{short_uid()}" if template_path is not None: template = load_template_file(template_path) template_rendered = render_template(template, **(template_mapping or {})) response = cfn_client.create_change_set( StackName=stack_name, ChangeSetName=change_set_name, TemplateBody=template_rendered, Capabilities=[ "CAPABILITY_AUTO_EXPAND", "CAPABILITY_IAM", "CAPABILITY_NAMED_IAM" ], 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), _max_wait=60) cfn_client.execute_change_set(ChangeSetName=change_set_id) assert wait_until(is_change_set_finished(change_set_id), _max_wait=60) 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}) 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 return DeployResult(change_set_id, stack_id, stack_name, change_set_name, mapped_outputs, _destroy_stack)