def _put_attribute(self, cluster_name, name, value=None, target_id=None, target_type=None): if target_id is None and target_type is None: for instance in self.container_instances[cluster_name].values(): instance.attributes[name] = value elif target_type is None: # targetId is full container instance arn try: arn = target_id.rsplit('/', 1)[-1] self.container_instances[cluster_name][arn].attributes[ name] = value except KeyError: raise JsonRESTError('TargetNotFoundException', 'Could not find {0}'.format(target_id)) else: # targetId is container uuid, targetType must be container-instance try: if target_type != 'container-instance': raise JsonRESTError('TargetNotFoundException', 'Could not find {0}'.format(target_id)) self.container_instances[cluster_name][target_id].attributes[ name] = value except KeyError: raise JsonRESTError('TargetNotFoundException', 'Could not find {0}'.format(target_id))
def _delete_attribute( self, cluster_name, name, value=None, target_id=None, target_type=None ): if target_id is None and target_type is None: for instance in self.container_instances[cluster_name].values(): if name in instance.attributes and instance.attributes[name] == value: del instance.attributes[name] elif target_type is None: # targetId is full container instance arn try: arn = target_id.rsplit("/", 1)[-1] instance = self.container_instances[cluster_name][arn] if name in instance.attributes and instance.attributes[name] == value: del instance.attributes[name] except KeyError: raise JsonRESTError( "TargetNotFoundException", "Could not find {0}".format(target_id) ) else: # targetId is container uuid, targetType must be container-instance try: if target_type != "container-instance": raise JsonRESTError( "TargetNotFoundException", "Could not find {0}".format(target_id), ) instance = self.container_instances[cluster_name][target_id] if name in instance.attributes and instance.attributes[name] == value: del instance.attributes[name] except KeyError: raise JsonRESTError( "TargetNotFoundException", "Could not find {0}".format(target_id) )
def put_permission(self, event_bus_name, action, principal, statement_id): if not event_bus_name: event_bus_name = "default" event_bus = self.describe_event_bus(event_bus_name) if action is None or action != "events:PutEvents": raise JsonRESTError( "ValidationException", "Provided value in parameter 'action' is not supported.", ) if principal is None or self.ACCOUNT_ID.match(principal) is None: raise JsonRESTError("InvalidParameterValue", r"Principal must match ^(\d{1,12}|\*)$") if statement_id is None or self.STATEMENT_ID.match( statement_id) is None: raise JsonRESTError( "InvalidParameterValue", r"StatementId must match ^[a-zA-Z0-9-_]{1,64}$") event_bus._permissions[statement_id] = { "Action": action, "Principal": principal, }
def delete_attributes(self, cluster_name, attributes=None): if cluster_name is None or cluster_name not in self.clusters: raise JsonRESTError('ClusterNotFoundException', 'Cluster not found', status=400) if attributes is None: raise JsonRESTError('InvalidParameterException', 'attributes value is required') for attr in attributes: self._delete_attribute(cluster_name, attr['name'], attr.get('value'), attr.get('targetId'), attr.get('targetType'))
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: raise JsonRESTError("ValidationError", "Can only submit 10 events at once") # We dont really need to store the events yet return []
def update_ttl(self, table_name, ttl_spec): table = self.tables.get(table_name) if table is None: raise JsonRESTError('ResourceNotFound', 'Table not found') if 'Enabled' not in ttl_spec or 'AttributeName' not in ttl_spec: raise JsonRESTError('InvalidParameterValue', 'TimeToLiveSpecification does not contain Enabled and AttributeName') if ttl_spec['Enabled']: table.ttl['TimeToLiveStatus'] = 'ENABLED' else: table.ttl['TimeToLiveStatus'] = 'DISABLED' table.ttl['AttributeName'] = ttl_spec['AttributeName']
def remove_permission(self, event_bus_name, statement_id): if not event_bus_name: event_bus_name = "default" event_bus = self.describe_event_bus(event_bus_name) if not len(event_bus._permissions): raise JsonRESTError("ResourceNotFoundException", "EventBus does not have a policy.") if not event_bus._permissions.pop(statement_id, None): raise JsonRESTError( "ResourceNotFoundException", "Statement with the provided id does not exist.", )
def tag_resource(self, arn, tags): name = arn.split("/")[-1] if name in self.rules: self.tagger.tag_resource(self.rules[name].arn, tags) return {} raise JsonRESTError("ResourceNotFoundException", "An entity that you specified does not exist.")
def delete_event_bus(self, name): if name == "default": raise JsonRESTError( "ValidationException", "Cannot delete event bus default." ) self.event_buses.pop(name, None)
def describe_container_instances(self, cluster_str, list_container_instance_ids): cluster_name = cluster_str.split("/")[-1] if cluster_name not in self.clusters: raise Exception("{0} is not a cluster".format(cluster_name)) if not list_container_instance_ids: raise JsonRESTError( "InvalidParameterException", "Container instance cannot be empty" ) failures = [] container_instance_objects = [] for container_instance_id in list_container_instance_ids: container_instance_id = container_instance_id.split("/")[-1] container_instance = self.container_instances[cluster_name].get( container_instance_id, None ) if container_instance is not None: container_instance_objects.append(container_instance) else: failures.append( ContainerInstanceFailure( "MISSING", container_instance_id, self.region_name ) ) return container_instance_objects, failures
def list_resource_tags(self, key_id): if key_id in self.keys: return self.tagger.list_tags_for_resource(key_id) raise JsonRESTError( "NotFoundException", "The request was rejected because the specified entity or resource could not be found.", )
def list_tags_for_resource(self, arn): name = arn.split("/")[-1] if name in self.rules: return self.tagger.list_tags_for_resource(self.rules[name].arn) raise JsonRESTError( "ResourceNotFoundException", "An entity that you specified does not exist." )
def update_time_to_live(self, table_name, ttl_spec): table = self.tables.get(table_name) if table is None: raise JsonRESTError("ResourceNotFound", "Table not found") if "Enabled" not in ttl_spec or "AttributeName" not in ttl_spec: raise JsonRESTError( "InvalidParameterValue", "TimeToLiveSpecification does not contain Enabled and AttributeName", ) if ttl_spec["Enabled"]: table.ttl["TimeToLiveStatus"] = "ENABLED" else: table.ttl["TimeToLiveStatus"] = "DISABLED" table.ttl["AttributeName"] = ttl_spec["AttributeName"]
def list_attributes(self, target_type, cluster_name=None, attr_name=None, attr_value=None, max_results=None, next_token=None): if target_type != 'container-instance': raise JsonRESTError('InvalidParameterException', 'targetType must be container-instance') filters = [lambda x: True] # item will be {0 cluster_name, 1 arn, 2 name, 3 value} if cluster_name is not None: filters.append(lambda item: item[0] == cluster_name) if attr_name: filters.append(lambda item: item[2] == attr_name) if attr_name: filters.append(lambda item: item[3] == attr_value) all_attrs = [] for cluster_name, cobj in self.container_instances.items(): for container_instance in cobj.values(): for key, value in container_instance.attributes.items(): all_attrs.append( (cluster_name, container_instance.container_instance_arn, key, value)) return filter(lambda x: all(f(x) for f in filters), all_attrs)
def untag_resource(self, key_id, tag_names): if key_id in self.keys: self.tagger.untag_resource_using_names(key_id, tag_names) return {} raise JsonRESTError( "NotFoundException", "The request was rejected because the specified entity or resource could not be found.", )
def _parse_resource_arn(resource_arn): match = re.match( "^arn:aws:ecs:(?P<region>[^:]+):(?P<account_id>[^:]+):(?P<service>[^:]+)/(?P<id>.*)$", resource_arn) if not match: raise JsonRESTError('InvalidParameterException', 'The ARN provided is invalid.') return match.groupdict()
def tag_resource(self, key_id_or_arn, tags): key_id = self.get_key_id(key_id_or_arn) if key_id in self.keys: self.tagger.tag_resource(key_id, tags) return {} raise JsonRESTError( "NotFoundException", "The request was rejected because the specified entity or resource could not be found.", )
def put_permission(self, action, principal, statement_id): if action is None or action != "events:PutEvents": raise JsonRESTError("InvalidParameterValue", "Action must be PutEvents") if principal is None or self.ACCOUNT_ID.match(principal) is None: raise JsonRESTError("InvalidParameterValue", "Principal must match ^(\d{1,12}|\*)$") if statement_id is None or self.STATEMENT_ID.match( statement_id) is None: raise JsonRESTError( "InvalidParameterValue", "StatementId must match ^[a-zA-Z0-9-_]{1,64}$") self.permissions[statement_id] = { "action": action, "principal": principal }
def put_permission(self, action, principal, statement_id): if action is None or action != 'PutEvents': raise JsonRESTError('InvalidParameterValue', 'Action must be PutEvents') if principal is None or self.ACCOUNT_ID.match(principal) is None: raise JsonRESTError('InvalidParameterValue', 'Principal must match ^(\d{1,12}|\*)$') if statement_id is None or self.STATEMENT_ID.match( statement_id) is None: raise JsonRESTError( 'InvalidParameterValue', 'StatementId must match ^[a-zA-Z0-9-_]{1,64}$') self.permissions[statement_id] = { 'action': action, 'principal': principal }
def delete_attributes(self, cluster_name, attributes=None): if cluster_name is None or cluster_name not in self.clusters: raise JsonRESTError( "ClusterNotFoundException", "Cluster not found", status=400 ) if attributes is None: raise JsonRESTError( "InvalidParameterException", "attributes value is required" ) for attr in attributes: self._delete_attribute( cluster_name, attr["name"], attr.get("value"), attr.get("targetId"), attr.get("targetType"), )
def remove_targets(self, name, ids): rule = self.rules.get(name) if rule: rule.remove_targets(ids) return {"FailedEntries": [], "FailedEntryCount": 0} else: raise JsonRESTError( "ResourceNotFoundException", "An entity that you specified does not exist", )
def extract_keys_for_schema(cls, item: Dict, key_schema: List[Dict]): result = {} for key in key_schema: attr_name = key["AttributeName"] if attr_name not in item: raise JsonRESTError( error_type="ValidationException", message="One of the required keys was not given a value", ) result[attr_name] = item[attr_name] return result
def create_event_bus(self, name, event_source_name=None): if name in self.event_buses: raise JsonRESTError( "ResourceAlreadyExistsException", "Event bus {} already exists.".format(name), ) if not event_source_name and "/" in name: raise JsonRESTError("ValidationException", "Event bus name must not contain '/'.") if event_source_name and event_source_name not in self.event_sources: raise JsonRESTError( "ResourceNotFoundException", "Event source {} does not exist.".format(event_source_name), ) self.event_buses[name] = EventBus(self.region_name, name) return self.event_buses[name]
def describe_event_bus(self, name): if not name: name = "default" event_bus = self.event_buses.get(name) if not event_bus: raise JsonRESTError("ResourceNotFoundException", "Event bus {} does not exist.".format(name)) return event_bus
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 list_tags_for_resource(self, resource_arn): """Currently only implemented for task definitions""" match = re.match( "^arn:aws:ecs:(?P<region>[^:]+):(?P<account_id>[^:]+):(?P<service>[^:]+)/(?P<id>.*)$", resource_arn) if not match: raise JsonRESTError('InvalidParameterException', 'The ARN provided is invalid.') service = match.group("service") if service == "task-definition": for task_definition in self.task_definitions.values(): for revision in task_definition.values(): if revision.arn == resource_arn: return revision.tags else: raise TaskDefinitionNotFoundException() raise NotImplementedError()
def describe_ttl(self, table_name): table = self.tables.get(table_name) if table is None: raise JsonRESTError('ResourceNotFound', 'Table not found') return table.ttl
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
def remove_permission(self, statement_id): try: del self.permissions[statement_id] except KeyError: raise JsonRESTError('ResourceNotFoundException', 'StatementId not found')
def describe_time_to_live(self, table_name): table = self.tables.get(table_name) if table is None: raise JsonRESTError("ResourceNotFound", "Table not found") return table.ttl