Add multi region support for Events

This commit is contained in:
Jessie Nadler 2019-10-03 15:11:09 -04:00
parent 4fe66f521d
commit 277cec0928
4 changed files with 49 additions and 28 deletions

View File

@ -1,6 +1,7 @@
from __future__ import unicode_literals from __future__ import unicode_literals
from .models import events_backend from .models import events_backends
from ..core.models import base_decorator
events_backends = {"global": events_backend} events_backend = events_backends['us-east-1']
mock_events = events_backend.decorator mock_events = base_decorator(events_backends)

View File

@ -1,6 +1,7 @@
import os import os
import re import re
import json import json
import boto3
from moto.core.exceptions import JsonRESTError from moto.core.exceptions import JsonRESTError
from moto.core import BaseBackend, BaseModel from moto.core import BaseBackend, BaseModel
@ -8,12 +9,15 @@ from moto.core import BaseBackend, BaseModel
class Rule(BaseModel): class Rule(BaseModel):
def _generate_arn(self, name): def _generate_arn(self, name, region_name):
return 'arn:aws:events:us-west-2:111111111111:rule/' + name return 'arn:aws:events:{region_name}:111111111111:rule/{name}'.format(
region_name=region_name,
name=name
)
def __init__(self, name, **kwargs): def __init__(self, name, region_name, **kwargs):
self.name = name self.name = name
self.arn = kwargs.get('Arn') or self._generate_arn(name) self.arn = kwargs.get('Arn') or self._generate_arn(name, region_name)
self.event_pattern = kwargs.get('EventPattern') self.event_pattern = kwargs.get('EventPattern')
self.schedule_exp = kwargs.get('ScheduleExpression') self.schedule_exp = kwargs.get('ScheduleExpression')
self.state = kwargs.get('State') or 'ENABLED' self.state = kwargs.get('State') or 'ENABLED'
@ -55,15 +59,20 @@ class EventsBackend(BaseBackend):
ACCOUNT_ID = re.compile(r'^(\d{1,12}|\*)$') ACCOUNT_ID = re.compile(r'^(\d{1,12}|\*)$')
STATEMENT_ID = re.compile(r'^[a-zA-Z0-9-_]{1,64}$') STATEMENT_ID = re.compile(r'^[a-zA-Z0-9-_]{1,64}$')
def __init__(self): def __init__(self, region_name):
self.rules = {} self.rules = {}
# This array tracks the order in which the rules have been added, since # This array tracks the order in which the rules have been added, since
# 2.6 doesn't have OrderedDicts. # 2.6 doesn't have OrderedDicts.
self.rules_order = [] self.rules_order = []
self.next_tokens = {} self.next_tokens = {}
self.region_name = region_name
self.permissions = {} self.permissions = {}
def reset(self):
region_name = self.region_name
self.__dict__ = {}
self.__init__(region_name)
def _get_rule_by_index(self, i): def _get_rule_by_index(self, i):
return self.rules.get(self.rules_order[i]) return self.rules.get(self.rules_order[i])
@ -173,7 +182,7 @@ class EventsBackend(BaseBackend):
return return_obj return return_obj
def put_rule(self, name, **kwargs): def put_rule(self, name, **kwargs):
rule = Rule(name, **kwargs) rule = Rule(name, self.region_name, **kwargs)
self.rules[rule.name] = rule self.rules[rule.name] = rule
self.rules_order.append(rule.name) self.rules_order.append(rule.name)
return rule.arn return rule.arn
@ -229,7 +238,7 @@ class EventsBackend(BaseBackend):
raise JsonRESTError('ResourceNotFoundException', 'StatementId not found') raise JsonRESTError('ResourceNotFoundException', 'StatementId not found')
def describe_event_bus(self): def describe_event_bus(self):
arn = "arn:aws:events:us-east-1:000000000000:event-bus/default" arn = "arn:aws:events:{0}:000000000000:event-bus/default".format(self.region_name)
statements = [] statements = []
for statement_id, data in self.permissions.items(): for statement_id, data in self.permissions.items():
statements.append({ statements.append({
@ -248,4 +257,5 @@ class EventsBackend(BaseBackend):
} }
events_backend = EventsBackend() available_regions = boto3.session.Session().get_available_regions("events")
events_backends = {region: EventsBackend(region) for region in available_regions}

View File

@ -2,11 +2,21 @@ import json
import re import re
from moto.core.responses import BaseResponse from moto.core.responses import BaseResponse
from moto.events import events_backend from moto.events import events_backends
class EventsHandler(BaseResponse): class EventsHandler(BaseResponse):
@property
def events_backend(self):
"""
Events Backend
:return: Events Backend object
:rtype: moto.events.models.EventsBackend
"""
return events_backends[self.region]
def _generate_rule_dict(self, rule): def _generate_rule_dict(self, rule):
return { return {
'Name': rule.name, 'Name': rule.name,
@ -40,7 +50,7 @@ class EventsHandler(BaseResponse):
if not name: if not name:
return self.error('ValidationException', 'Parameter Name is required.') return self.error('ValidationException', 'Parameter Name is required.')
events_backend.delete_rule(name) self.events_backend.delete_rule(name)
return '', self.response_headers return '', self.response_headers
@ -50,7 +60,7 @@ class EventsHandler(BaseResponse):
if not name: if not name:
return self.error('ValidationException', 'Parameter Name is required.') return self.error('ValidationException', 'Parameter Name is required.')
rule = events_backend.describe_rule(name) rule = self.events_backend.describe_rule(name)
if not rule: if not rule:
return self.error('ResourceNotFoundException', 'Rule test does not exist.') return self.error('ResourceNotFoundException', 'Rule test does not exist.')
@ -64,7 +74,7 @@ class EventsHandler(BaseResponse):
if not name: if not name:
return self.error('ValidationException', 'Parameter Name is required.') return self.error('ValidationException', 'Parameter Name is required.')
if not events_backend.disable_rule(name): if not self.events_backend.disable_rule(name):
return self.error('ResourceNotFoundException', 'Rule ' + name + ' does not exist.') return self.error('ResourceNotFoundException', 'Rule ' + name + ' does not exist.')
return '', self.response_headers return '', self.response_headers
@ -75,7 +85,7 @@ class EventsHandler(BaseResponse):
if not name: if not name:
return self.error('ValidationException', 'Parameter Name is required.') return self.error('ValidationException', 'Parameter Name is required.')
if not events_backend.enable_rule(name): if not self.events_backend.enable_rule(name):
return self.error('ResourceNotFoundException', 'Rule ' + name + ' does not exist.') return self.error('ResourceNotFoundException', 'Rule ' + name + ' does not exist.')
return '', self.response_headers return '', self.response_headers
@ -91,7 +101,7 @@ class EventsHandler(BaseResponse):
if not target_arn: if not target_arn:
return self.error('ValidationException', 'Parameter TargetArn is required.') return self.error('ValidationException', 'Parameter TargetArn is required.')
rule_names = events_backend.list_rule_names_by_target( rule_names = self.events_backend.list_rule_names_by_target(
target_arn, next_token, limit) target_arn, next_token, limit)
return json.dumps(rule_names), self.response_headers return json.dumps(rule_names), self.response_headers
@ -101,7 +111,7 @@ class EventsHandler(BaseResponse):
next_token = self._get_param('NextToken') next_token = self._get_param('NextToken')
limit = self._get_param('Limit') limit = self._get_param('Limit')
rules = events_backend.list_rules(prefix, next_token, limit) rules = self.events_backend.list_rules(prefix, next_token, limit)
rules_obj = {'Rules': []} rules_obj = {'Rules': []}
for rule in rules['Rules']: for rule in rules['Rules']:
@ -121,7 +131,7 @@ class EventsHandler(BaseResponse):
return self.error('ValidationException', 'Parameter Rule is required.') return self.error('ValidationException', 'Parameter Rule is required.')
try: try:
targets = events_backend.list_targets_by_rule( targets = self.events_backend.list_targets_by_rule(
rule_name, next_token, limit) rule_name, next_token, limit)
except KeyError: except KeyError:
return self.error('ResourceNotFoundException', 'Rule ' + rule_name + ' does not exist.') return self.error('ResourceNotFoundException', 'Rule ' + rule_name + ' does not exist.')
@ -131,7 +141,7 @@ class EventsHandler(BaseResponse):
def put_events(self): def put_events(self):
events = self._get_param('Entries') events = self._get_param('Entries')
failed_entries = events_backend.put_events(events) failed_entries = self.events_backend.put_events(events)
if failed_entries: if failed_entries:
return json.dumps({ return json.dumps({
@ -165,7 +175,7 @@ class EventsHandler(BaseResponse):
re.match('^rate\(\d*\s(minute|minutes|hour|hours|day|days)\)', sched_exp)): re.match('^rate\(\d*\s(minute|minutes|hour|hours|day|days)\)', sched_exp)):
return self.error('ValidationException', 'Parameter ScheduleExpression is not valid.') return self.error('ValidationException', 'Parameter ScheduleExpression is not valid.')
rule_arn = events_backend.put_rule( rule_arn = self.events_backend.put_rule(
name, name,
ScheduleExpression=sched_exp, ScheduleExpression=sched_exp,
EventPattern=event_pattern, EventPattern=event_pattern,
@ -186,7 +196,7 @@ class EventsHandler(BaseResponse):
if not targets: if not targets:
return self.error('ValidationException', 'Parameter Targets is required.') return self.error('ValidationException', 'Parameter Targets is required.')
if not events_backend.put_targets(rule_name, targets): if not self.events_backend.put_targets(rule_name, targets):
return self.error('ResourceNotFoundException', 'Rule ' + rule_name + ' does not exist.') return self.error('ResourceNotFoundException', 'Rule ' + rule_name + ' does not exist.')
return '', self.response_headers return '', self.response_headers
@ -201,7 +211,7 @@ class EventsHandler(BaseResponse):
if not ids: if not ids:
return self.error('ValidationException', 'Parameter Ids is required.') return self.error('ValidationException', 'Parameter Ids is required.')
if not events_backend.remove_targets(rule_name, ids): if not self.events_backend.remove_targets(rule_name, ids):
return self.error('ResourceNotFoundException', 'Rule ' + rule_name + ' does not exist.') return self.error('ResourceNotFoundException', 'Rule ' + rule_name + ' does not exist.')
return '', self.response_headers return '', self.response_headers
@ -214,16 +224,16 @@ class EventsHandler(BaseResponse):
principal = self._get_param('Principal') principal = self._get_param('Principal')
statement_id = self._get_param('StatementId') statement_id = self._get_param('StatementId')
events_backend.put_permission(action, principal, statement_id) self.events_backend.put_permission(action, principal, statement_id)
return '' return ''
def remove_permission(self): def remove_permission(self):
statement_id = self._get_param('StatementId') statement_id = self._get_param('StatementId')
events_backend.remove_permission(statement_id) self.events_backend.remove_permission(statement_id)
return '' return ''
def describe_event_bus(self): def describe_event_bus(self):
return json.dumps(events_backend.describe_event_bus()) return json.dumps(self.events_backend.describe_event_bus())

View File

@ -87,7 +87,7 @@ def test_describe_rule():
assert(response is not None) assert(response is not None)
assert(response.get('Name') == rule_name) assert(response.get('Name') == rule_name)
assert(response.get('Arn') is not None) assert(response.get('Arn') == 'arn:aws:events:us-west-2:111111111111:rule/{0}'.format(rule_name))
@mock_events @mock_events