def put_targets(self, name, event_bus_name, targets): # super simple ARN check invalid_arn = next( (target["Arn"] for target in targets if not re.match(r"arn:[\d\w:\-/]*", target["Arn"])), None, ) if invalid_arn: raise ValidationException( "Parameter {} is not valid. " "Reason: Provided Arn is not in correct format.".format( invalid_arn)) for target in targets: arn = target["Arn"] if (":sqs:" in arn and arn.endswith(".fifo") and not target.get("SqsParameters")): raise ValidationException( "Parameter(s) SqsParameters must be specified for target: {}." .format(target["Id"])) rule = self.rules.get(name) if not rule: raise ResourceNotFoundException( "Rule {0} does not exist on EventBus {1}.".format( name, event_bus_name)) rule.put_targets(targets)
def list_archives(self, name_prefix, source_arn, state): if [name_prefix, source_arn, state].count(None) < 2: raise ValidationException( "At most one filter is allowed for ListArchives. " "Use either : State, EventSourceArn, or NamePrefix.") if state and state not in Archive.VALID_STATES: raise ValidationException( "1 validation error detected: " "Value '{0}' at 'state' failed to satisfy constraint: " "Member must satisfy enum value set: " "[{1}]".format(state, ", ".join(Archive.VALID_STATES))) if [name_prefix, source_arn, state].count(None) == 3: return [ archive.describe_short() for archive in self.archives.values() ] result = [] for archive in self.archives.values(): if name_prefix and archive.name.startswith(name_prefix): result.append(archive.describe_short()) elif source_arn and archive.source_arn == source_arn: result.append(archive.describe_short()) elif state and archive.state == state: result.append(archive.describe_short()) return result
def list_replays(self, name_prefix, source_arn, state): if [name_prefix, source_arn, state].count(None) < 2: raise ValidationException( "At most one filter is allowed for ListReplays. " "Use either : State, EventSourceArn, or NamePrefix.") valid_states = sorted([item.value for item in ReplayState]) if state and state not in valid_states: raise ValidationException( "1 validation error detected: " "Value '{0}' at 'state' failed to satisfy constraint: " "Member must satisfy enum value set: " "[{1}]".format(state, ", ".join(valid_states))) if [name_prefix, source_arn, state].count(None) == 3: return [ replay.describe_short() for replay in self.replays.values() ] result = [] for replay in self.replays.values(): if name_prefix and replay.name.startswith(name_prefix): result.append(replay.describe_short()) elif source_arn and replay.source_arn == source_arn: result.append(replay.describe_short()) elif state and replay.state == state: result.append(replay.describe_short()) return result
def start_replay( self, name, description, source_arn, start_time, end_time, destination ): event_bus_arn = destination["Arn"] event_bus_arn_pattern = r"^arn:aws:events:[a-zA-Z0-9-]+:\d{12}:event-bus/" if not re.match(event_bus_arn_pattern, event_bus_arn): raise ValidationException( "Parameter Destination.Arn is not valid. " "Reason: Must contain an event bus ARN." ) self._get_event_bus(event_bus_arn) archive_name = source_arn.split("/")[-1] archive = self.archives.get(archive_name) if not archive: raise ValidationException( "Parameter EventSourceArn is not valid. " "Reason: Archive {} does not exist.".format(archive_name) ) if event_bus_arn != archive.source_arn: raise ValidationException( "Parameter Destination.Arn is not valid. " "Reason: Cross event bus replay is not permitted." ) if start_time > end_time: raise ValidationException( "Parameter EventEndTime is not valid. " "Reason: EventStartTime must be before EventEndTime." ) if name in self.replays: raise ResourceAlreadyExistsException( "Replay {} already exists.".format(name) ) replay = Replay( self.region_name, name, description, source_arn, start_time, end_time, destination, ) self.replays[name] = replay replay.replay_events(archive) return { "ReplayArn": replay.arn, "ReplayStartTime": replay.start_time, "State": ReplayState.STARTING.value, # the replay will be done before returning the response }
def create_archive(self, name, source_arn, description, event_pattern, retention): if len(name) > 48: raise ValidationException( " 1 validation error detected: " "Value '{}' at 'archiveName' failed to satisfy constraint: " "Member must have length less than or equal to 48".format( name)) if event_pattern: self._validate_event_pattern(event_pattern) event_bus_name = source_arn.split("/")[-1] if event_bus_name not in self.event_buses: raise ResourceNotFoundException( "Event bus {} does not exist.".format(event_bus_name)) if name in self.archives: raise ResourceAlreadyExistsException( "Archive {} already exists.".format(name)) archive = Archive(self.region_name, name, source_arn, description, event_pattern, retention) self.archives[name] = archive return archive
def create_archive(self, name, source_arn, description, event_pattern, retention): if len(name) > 48: raise ValidationException( " 1 validation error detected: " "Value '{}' at 'archiveName' failed to satisfy constraint: " "Member must have length less than or equal to 48".format(name) ) if event_pattern: self._validate_event_pattern(event_pattern) event_bus = self._get_event_bus(source_arn) if name in self.archives: raise ResourceAlreadyExistsException( "Archive {} already exists.".format(name) ) archive = Archive( self.region_name, name, source_arn, description, event_pattern, retention ) rule_event_pattern = json.loads(event_pattern or "{}") rule_event_pattern["replay-name"] = [{"exists": False}] rule = self.put_rule( "Events-Archive-{}".format(name), **{ "EventPattern": json.dumps(rule_event_pattern), "EventBusName": event_bus.name, "ManagedBy": "prod.vhs.events.aws.internal", } ) self.put_targets( rule.name, rule.event_bus_name, [ { "Id": rule.name, "Arn": "arn:aws:events:{}:::".format(self.region_name), "InputTransformer": { "InputPathsMap": {}, "InputTemplate": json.dumps( { "archive-arn": "{0}:{1}".format( archive.arn, archive.uuid ), "event": "<aws.events.event.json>", "ingestion-time": "<aws.events.event.ingestion-time>", } ), }, } ], ) self.archives[name] = archive return archive
def put_rule(self, name, **kwargs): if kwargs.get("ScheduleExpression") and kwargs.get("EventBusName") != "default": raise ValidationException( "ScheduleExpression is supported only on the default event bus." ) if name in self.rules: self.update_rule(self.rules[name], **kwargs) new_rule = self.rules[name] else: new_rule = Rule(name, self.region_name, **kwargs) self.rules[new_rule.name] = new_rule self.rules_order.append(new_rule.name) return new_rule
def put_events(self, events): num_events = len(events) if num_events < 1: raise JsonRESTError("ValidationError", "Need at least 1 event") elif num_events > 10: # the exact error text is longer, the Value list consists of all the put events raise ValidationException( "1 validation error detected: " "Value '[PutEventsRequestEntry]' at 'entries' failed to satisfy constraint: " "Member must have length less than or equal to 10") entries = [] for event in events: if "Source" not in event: entries.append({ "ErrorCode": "InvalidArgument", "ErrorMessage": "Parameter Source is not valid. Reason: Source is a required argument.", }) elif "DetailType" not in event: entries.append({ "ErrorCode": "InvalidArgument", "ErrorMessage": "Parameter DetailType is not valid. Reason: DetailType is a required argument.", }) elif "Detail" not in event: entries.append({ "ErrorCode": "InvalidArgument", "ErrorMessage": "Parameter Detail is not valid. Reason: Detail is a required argument.", }) else: try: json.loads(event["Detail"]) except ValueError: # json.JSONDecodeError exists since Python 3.5 entries.append({ "ErrorCode": "MalformedDetail", "ErrorMessage": "Detail is malformed.", }) continue entries.append({"EventId": str(uuid4())}) # We dont really need to store the events yet return entries
def put_targets(self, name, event_bus_name, targets): # super simple ARN check invalid_arn = next( ( target["Arn"] for target in targets if not re.match(r"arn:[\d\w:\-/]*", target["Arn"]) ), None, ) if invalid_arn: raise ValidationException( "Parameter {} is not valid. " "Reason: Provided Arn is not in correct format.".format(invalid_arn) ) rule = self.rules.get(name) if not rule: raise ResourceNotFoundException( "Rule {0} does not exist on EventBus {1}.".format(name, event_bus_name) ) rule.put_targets(targets)
def put_events(self, events): num_events = len(events) if num_events > 10: # the exact error text is longer, the Value list consists of all the put events raise ValidationException( "1 validation error detected: " "Value '[PutEventsRequestEntry]' at 'entries' failed to satisfy constraint: " "Member must have length less than or equal to 10") entries = [] for event in events: if "Source" not in event: entries.append({ "ErrorCode": "InvalidArgument", "ErrorMessage": "Parameter Source is not valid. Reason: Source is a required argument.", }) elif "DetailType" not in event: entries.append({ "ErrorCode": "InvalidArgument", "ErrorMessage": "Parameter DetailType is not valid. Reason: DetailType is a required argument.", }) elif "Detail" not in event: entries.append({ "ErrorCode": "InvalidArgument", "ErrorMessage": "Parameter Detail is not valid. Reason: Detail is a required argument.", }) else: try: json.loads(event["Detail"]) except ValueError: # json.JSONDecodeError exists since Python 3.5 entries.append({ "ErrorCode": "MalformedDetail", "ErrorMessage": "Detail is malformed.", }) continue event_id = str(uuid4()) entries.append({"EventId": event_id}) # if 'EventBusName' is not especially set, it will be sent to the default one event_bus_name = event.get("EventBusName", "default") for rule in self.rules.values(): rule.send_to_targets( event_bus_name, { "version": "0", "id": event_id, "detail-type": event["DetailType"], "source": event["Source"], "account": ACCOUNT_ID, "time": event.get("Time", unix_time(datetime.utcnow())), "region": self.region_name, "resources": event.get("Resources", []), "detail": json.loads(event["Detail"]), }, ) return entries
def put_events(self, events): num_events = len(events) if num_events < 1: raise JsonRESTError("ValidationError", "Need at least 1 event") elif num_events > 10: # the exact error text is longer, the Value list consists of all the put events raise ValidationException( "1 validation error detected: " "Value '[PutEventsRequestEntry]' at 'entries' failed to satisfy constraint: " "Member must have length less than or equal to 10") entries = [] for event in events: if "Source" not in event: entries.append({ "ErrorCode": "InvalidArgument", "ErrorMessage": "Parameter Source is not valid. Reason: Source is a required argument.", }) elif "DetailType" not in event: entries.append({ "ErrorCode": "InvalidArgument", "ErrorMessage": "Parameter DetailType is not valid. Reason: DetailType is a required argument.", }) elif "Detail" not in event: entries.append({ "ErrorCode": "InvalidArgument", "ErrorMessage": "Parameter Detail is not valid. Reason: Detail is a required argument.", }) else: try: json.loads(event["Detail"]) except ValueError: # json.JSONDecodeError exists since Python 3.5 entries.append({ "ErrorCode": "MalformedDetail", "ErrorMessage": "Detail is malformed.", }) continue entries.append({"EventId": str(uuid4())}) # add to correct archive # if 'EventBusName' is not espically set, it will stored in the default event_bus_name = event.get("EventBusName", "default") archives = [ archive for archive in self.archives.values() if archive.event_bus_name == event_bus_name ] for archive in archives: event_copy = copy.deepcopy(event) event_copy.pop("EventBusName", None) if archive.matches_pattern(event): archive.events.append(event_copy) # We dont really need to store the events yet return entries