MQ - Initial implementation (#4789)

This commit is contained in:
Bert Blommers 2022-01-25 19:01:03 -01:00 committed by GitHub
parent 35d3c72039
commit cf50da6938
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 2118 additions and 1 deletions

View File

@ -3587,6 +3587,34 @@
- [X] put_object
</details>
## mq
<details>
<summary>86% implemented</summary>
- [X] create_broker
- [X] create_configuration
- [X] create_tags
- [X] create_user
- [X] delete_broker
- [X] delete_tags
- [X] delete_user
- [X] describe_broker
- [ ] describe_broker_engine_types
- [ ] describe_broker_instance_options
- [X] describe_configuration
- [X] describe_configuration_revision
- [X] describe_user
- [X] list_brokers
- [ ] list_configuration_revisions
- [X] list_configurations
- [X] list_tags
- [X] list_users
- [X] reboot_broker
- [X] update_broker
- [X] update_configuration
- [X] update_user
</details>
## opsworks
<details>
<summary>12% implemented</summary>
@ -5372,7 +5400,6 @@
- migrationhub-config
- migrationhubstrategy
- mobile
- mq
- mturk
- mwaa
- neptune

64
docs/docs/services/mq.rst Normal file
View File

@ -0,0 +1,64 @@
.. _implementedservice_mq:
.. |start-h3| raw:: html
<h3>
.. |end-h3| raw:: html
</h3>
==
mq
==
.. autoclass:: moto.mq.models.MQBackend
|start-h3| Example usage |end-h3|
.. sourcecode:: python
@mock_mq
def test_mq_behaviour:
boto3.client("mq")
...
|start-h3| Implemented features for this service |end-h3|
- [X] create_broker
- [X] create_configuration
- [X] create_tags
- [X] create_user
- [X] delete_broker
- [X] delete_tags
- [X] delete_user
- [X] describe_broker
- [ ] describe_broker_engine_types
- [ ] describe_broker_instance_options
- [X] describe_configuration
- [X] describe_configuration_revision
- [X] describe_user
- [X] list_brokers
Pagination is not yet implemented
- [ ] list_configuration_revisions
- [X] list_configurations
Pagination has not yet been implemented.
- [X] list_tags
- [X] list_users
- [X] reboot_broker
- [X] update_broker
- [X] update_configuration
No validation occurs on the provided XML. The authenticationStrategy may be changed depending on the provided configuration.
- [X] update_user

View File

@ -84,6 +84,7 @@ mock_kinesis = lazy_load(".kinesis", "mock_kinesis")
mock_kms = lazy_load(".kms", "mock_kms")
mock_logs = lazy_load(".logs", "mock_logs")
mock_managedblockchain = lazy_load(".managedblockchain", "mock_managedblockchain")
mock_mq = lazy_load(".mq", "mock_mq", boto3_name="mq")
mock_opsworks = lazy_load(".opsworks", "mock_opsworks")
mock_organizations = lazy_load(".organizations", "mock_organizations")
mock_polly = lazy_load(".polly", "mock_polly")

View File

@ -91,6 +91,7 @@ backend_url_patterns = [
("mediapackage", re.compile("https?://mediapackage\\.(.+)\\.amazonaws.com")),
("mediastore", re.compile("https?://mediastore\\.(.+)\\.amazonaws\\.com")),
("mediastore-data", re.compile("https?://data.mediastore\\.(.+)\\.amazonaws.com")),
("mq", re.compile("https?://mq\\.(.+)\\.amazonaws\\.com")),
("opsworks", re.compile("https?://opsworks\\.us-east-1\\.amazonaws.com")),
("organizations", re.compile("https?://organizations\\.(.+)\\.amazonaws\\.com")),
("polly", re.compile("https?://polly\\.(.+)\\.amazonaws.com")),

5
moto/mq/__init__.py Normal file
View File

@ -0,0 +1,5 @@
"""mq module initialization; sets value for base decorator."""
from .models import mq_backends
from ..core.models import base_decorator
mock_mq = base_decorator(mq_backends)

183
moto/mq/configuration.py Normal file
View File

@ -0,0 +1,183 @@
DEFAULT_CONFIGURATION_DATA = """<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<broker xmlns="http://activemq.apache.org/schema/core" schedulePeriodForDestinationPurge="10000">
<!--
A configuration contains all of the settings for your ActiveMQ broker, in XML format (similar to ActiveMQ's activemq.xml file).
You can create a configuration before creating any brokers. You can then apply the configuration to one or more brokers.
You can use additional attributes for the broker element above. These attributes allow you to configure broker-wide settings.
For more information, see Configuration and Amazon MQ Broker Configuration Parameters in the Amazon MQ Developer Guide:
https://docs.aws.amazon.com/amazon-mq/latest/developer-guide/amazon-mq-broker-configuration-parameters.html
-->
<!--
Mirrored queues let you send a copy of each message to a topic with a similar name automatically.
For more information, see http://activemq.apache.org/mirrored-queues.html
Virtual destinations let you configure advanced routing of messages between destinations.
For more information, see http://activemq.apache.org/virtual-destinations.html
-->
<!--
<destinationInterceptors>
<mirroredQueue copyMessage="true" postfix=".qmirror" prefix=""/>
<virtualDestinationInterceptor>
<virtualDestinations>
<virtualTopic name="&gt;" prefix="VirtualTopicConsumers.*." selectorAware="false"/>
<compositeQueue name="MY.QUEUE">
<forwardTo>
<queue physicalName="FOO"/>
<topic physicalName="BAR"/>
</forwardTo>
</compositeQueue>
</virtualDestinations>
</virtualDestinationInterceptor>
</destinationInterceptors>
-->
<!--
By default, Amazon MQ optimizes for queues with fast consumers:
Consumers are considered fast if they are able to keep up with the rate of messages generated by producers.
Consumers are considered slow if a queue builds up a backlog of unacknowledged messages, potentially causing a decrease in producer throughput.
To instruct Amazon MQ to optimize for queues with slow consumers, set the concurrentStoreAndDispatchQueues attribute to false.
For more information, see https://docs.aws.amazon.com/amazon-mq/latest/developer-guide/ensuring-effective-amazon-mq-performance.html
-->
<!--
<persistenceAdapter>
<kahaDB concurrentStoreAndDispatchQueues="false"/>
</persistenceAdapter>
-->
<destinationPolicy>
<policyMap>
<policyEntries>
<!--
gcInactiveDestinations is used to automatically purge inactive destinations
preventing them from unnecessarily using broker resources.
An 'inactive' destination is one that has no messages pending and no consumers connected.
For more information, see: http://activemq.apache.org/delete-inactive-destinations.html
-->
<policyEntry topic="&gt;" gcInactiveDestinations="true" inactiveTimoutBeforeGC="600000">
<!--
The constantPendingMessageLimitStrategy is used to prevent
slow topic consumers to block producers and affect other consumers
by limiting the number of messages that are retained
For more information, see: http://activemq.apache.org/slow-consumer-handling.html
-->
<pendingMessageLimitStrategy>
<constantPendingMessageLimitStrategy limit="1000"/>
</pendingMessageLimitStrategy>
</policyEntry>
<policyEntry queue="&gt;" gcInactiveDestinations="true" inactiveTimoutBeforeGC="600000" />
<!--
Destination policies let you configure a rich set of behaviors for your queues and topics.
For more information, see http://activemq.apache.org/per-destination-policies.html
-->
<!--
<policyEntry topic="FOO.&gt;">
<dispatchPolicy>
<roundRobinDispatchPolicy/>
</dispatchPolicy>
<subscriptionRecoveryPolicy>
<lastImageSubscriptionRecoveryPolicy/>
</subscriptionRecoveryPolicy>
</policyEntry>
<policyEntry advisoryForConsumed="true" tempTopic="true"/>
<policyEntry advisoryForConsumed="true" tempQueue="true"/>
-->
</policyEntries>
</policyMap>
</destinationPolicy>
<!--
Typically, destinations are created automatically when they are used. Amazon MQ lets you create destinations when the broker is started.
For more information, see http://activemq.apache.org/configure-startup-destinations.html
-->
<!--
<destinations>
<queue physicalName="FOO.BAR"/>
<topic physicalName="SOME.TOPIC"/>
</destinations>
-->
<!--
You can control advanced ActiveMQ features using plugins.
-->
<plugins>
<!--
The Authorization plugin allows you to control the groups of users that are allowed to perform certain operations on your destinations.
For more information, see http://activemq.apache.org/security.html
-->
<!--
<authorizationPlugin>
<map>
<authorizationMap>
<authorizationEntries>
<authorizationEntry admin="guests,users" queue="GUEST.&gt;" read="guests" write="guests,users"/>
<authorizationEntry admin="guests,users" read="guests,users" topic="ActiveMQ.Advisory.&gt;" write="guests,users"/>
</authorizationEntries>
<tempDestinationAuthorizationEntry>
<tempDestinationAuthorizationEntry admin="tempDestinationAdmins" read="tempDestinationAdmins" write="tempDestinationAdmins"/>
</tempDestinationAuthorizationEntry>
</authorizationMap>
</map>
</authorizationPlugin>
-->
<!--
The Discarding DLQ plugin simplifies the configuration of your global dead-letter queue strategy.
You can take advantage of a more granular per-destination control by using destination policies.
For more information, see http://activemq.apache.org/message-redelivery-and-dlq-handling.html
-->
<!--
<discardingDLQBrokerPlugin dropAll="true" dropTemporaryQueues="true" dropTemporaryTopics="true"/>
-->
<!--
The Force Persistency Mode plugin can override the persistency mode set on messages.
-->
<!--
<forcePersistencyModeBrokerPlugin persistenceFlag="true"/>
-->
<!--
The Redelivery plugin extends the capabilities of destination policies with respect to message redelivery.
For more information, see http://activemq.apache.org/message-redelivery-and-dlq-handling.html
-->
<!--
<redeliveryPlugin fallbackToDeadLetter="true" sendToDlqIfMaxRetriesExceeded="true">
<redeliveryPolicyMap>
<redeliveryPolicyMap>
<redeliveryPolicyEntries>
<redeliveryPolicy maximumRedeliveries="4" queue="SpecialQueue" redeliveryDelay="10000"/>
</redeliveryPolicyEntries>
<defaultEntry>
<redeliveryPolicy initialRedeliveryDelay="5000" maximumRedeliveries="4" redeliveryDelay="10000"/>
</defaultEntry>
</redeliveryPolicyMap>
</redeliveryPolicyMap>
</redeliveryPlugin>
-->
<!--
The Statistics plugin lets you query broker or destination statistics by sending messages to the broker.
For more information, see http://activemq.apache.org/statisticsplugin.html
-->
<!--
<statisticsBrokerPlugin/>
-->
<!--
The Timestamping plugin lets the broker use server-side time instead of client-provided time for messages.
For more information, see http://activemq.apache.org/timestampplugin.html
-->
<!--
<timeStampingBrokerPlugin ttlCeiling="86400000" zeroExpirationOverride="86400000"/>
-->
</plugins>
<!--
Network connectors let you connect brokers into networks of brokers.
For more information, see Creating and Configuring an Amazon MQ Network of Brokers
(https://docs.aws.amazon.com/amazon-mq/latest/developer-guide/amazon-mq-creating-configuring-network-of-brokers.html)
in the Amazon MQ Developer Guide and also Networks of Brokers
(http://activemq.apache.org/networks-of-brokers.html) in the ActiveMQ documentation.
-->
<!--
<networkConnectors>
<networkConnector name="myNetworkConnector" userName="commonUser" uri="masterslave:(ssl://b-1a2b3c4d-1.mq.region.amazonaws.com:61617,ssl://b-1a2b3c4d-2.mq.region.amazonaws.com:61617)"/>
</networkConnectors>
-->
</broker>
"""

71
moto/mq/exceptions.py Normal file
View File

@ -0,0 +1,71 @@
import json
from moto.core.exceptions import JsonRESTError
class MQError(JsonRESTError):
pass
class UnknownBroker(MQError):
def __init__(self, broker_id):
super().__init__("NotFoundException", "Can't find requested broker")
self.broker_id = broker_id
def get_body(self):
body = {
"errorAttribute": "broker-id",
"message": f"Can't find requested broker [{self.broker_id}]. Make sure your broker exists.",
}
return json.dumps(body)
class UnknownConfiguration(MQError):
def __init__(self, config_id):
super().__init__("NotFoundException", "Can't find requested configuration")
self.config_id = config_id
def get_body(self):
body = {
"errorAttribute": "configuration_id",
"message": f"Can't find requested configuration [{self.config_id}]. Make sure your configuration exists.",
}
return json.dumps(body)
class UnknownUser(MQError):
def __init__(self, username):
super().__init__("NotFoundException", "Can't find requested user")
self.username = username
def get_body(self):
body = {
"errorAttribute": "username",
"message": f"Can't find requested user [{self.username}]. Make sure your user exists.",
}
return json.dumps(body)
class UnsupportedEngineType(MQError):
def __init__(self, engine_type):
super().__init__("BadRequestException", "")
self.engine_type = engine_type
def get_body(self):
body = {
"errorAttribute": "engineType",
"message": f"Broker engine type [{self.engine_type}] does not support configuration.",
}
return json.dumps(body)
class UnknownEngineType(MQError):
def __init__(self, engine_type):
super().__init__("BadRequestException", "")
self.engine_type = engine_type
def get_body(self):
body = {
"errorAttribute": "engineType",
"message": f"Broker engine type [{self.engine_type}] is invalid. Valid values are: [ACTIVEMQ]",
}
return json.dumps(body)

528
moto/mq/models.py Normal file
View File

@ -0,0 +1,528 @@
import base64
import xmltodict
from moto.core import ACCOUNT_ID, BaseBackend, BaseModel
from moto.core.utils import BackendDict, get_random_hex, unix_time
from moto.utilities.tagging_service import TaggingService
from .configuration import DEFAULT_CONFIGURATION_DATA
from .exceptions import (
UnknownBroker,
UnknownConfiguration,
UnknownUser,
UnsupportedEngineType,
UnknownEngineType,
)
class ConfigurationRevision(BaseModel):
def __init__(self, configuration_id, revision_id, description, data=None):
self.configuration_id = configuration_id
self.created = unix_time()
self.description = description
self.is_invalid = False
self.revision_id = revision_id
if data is None:
self.data = base64.b64encode(
DEFAULT_CONFIGURATION_DATA.encode("UTF-8")
).decode("utf-8")
else:
self.data = data
def has_ldap_auth(self):
try:
xml = base64.b64decode(self.data)
dct = xmltodict.parse(xml, dict_constructor=dict)
return (
"cachedLDAPAuthorizationMap"
in dct["broker"]["plugins"]["authorizationPlugin"]["map"]
)
except Exception:
# There are many configurations to enable LDAP
# We're only checking for one here
# If anything fails, lets assume it's not LDAP
return False
def to_json(self, full=True):
resp = {
"created": self.created,
"description": self.description,
"revision": int(self.revision_id),
}
if full:
resp["configurationId"] = self.configuration_id
resp["data"] = self.data
return resp
class Configuration(BaseModel):
def __init__(self, region, name, engine_type, engine_version):
self.id = f"c-{get_random_hex(6)}"
self.arn = f"arn:aws:mq:{region}:{ACCOUNT_ID}:configuration:{self.id}"
self.created = unix_time()
self.name = name
self.engine_type = engine_type
self.engine_version = engine_version
self.revisions = dict()
default_desc = (
f"Auto-generated default for {self.name} on {engine_type} {engine_version}"
)
latest_revision = ConfigurationRevision(
configuration_id=self.id, revision_id="1", description=default_desc
)
self.revisions[latest_revision.revision_id] = latest_revision
self.authentication_strategy = (
"ldap" if latest_revision.has_ldap_auth() else "simple"
)
def update(self, data, description):
max_revision_id, _ = sorted(self.revisions.items())[-1]
next_revision_id = str(int(max_revision_id) + 1)
latest_revision = ConfigurationRevision(
configuration_id=self.id,
revision_id=next_revision_id,
description=description,
data=data,
)
self.revisions[next_revision_id] = latest_revision
self.authentication_strategy = (
"ldap" if latest_revision.has_ldap_auth() else "simple"
)
def get_revision(self, revision_id):
return self.revisions[revision_id]
def to_json(self):
_, latest_revision = sorted(self.revisions.items())[-1]
return {
"arn": self.arn,
"authenticationStrategy": self.authentication_strategy,
"created": self.created,
"engineType": self.engine_type,
"engineVersion": self.engine_version,
"id": self.id,
"name": self.name,
"latestRevision": latest_revision.to_json(full=False),
}
class User(BaseModel):
def __init__(self, broker_id, username, console_access=None, groups=None):
self.broker_id = broker_id
self.username = username
self.console_access = console_access or False
self.groups = groups or []
def update(self, console_access, groups):
if console_access is not None:
self.console_access = console_access
if groups:
self.groups = groups
def summary(self):
return {"username": self.username}
def to_json(self):
return {
"brokerId": self.broker_id,
"username": self.username,
"consoleAccess": self.console_access,
"groups": self.groups,
}
class Broker(BaseModel):
def __init__(
self,
name,
region,
authentication_strategy,
auto_minor_version_upgrade,
configuration,
deployment_mode,
encryption_options,
engine_type,
engine_version,
host_instance_type,
ldap_server_metadata,
logs,
maintenance_window_start_time,
publicly_accessible,
security_groups,
storage_type,
subnet_ids,
users,
):
self.name = name
self.id = get_random_hex(6)
self.arn = f"arn:aws:mq:{region}:{ACCOUNT_ID}:broker:{self.id}"
self.state = "RUNNING"
self.created = unix_time()
self.authentication_strategy = authentication_strategy
self.auto_minor_version_upgrade = auto_minor_version_upgrade
self.deployment_mode = deployment_mode
self.encryption_options = encryption_options
if not self.encryption_options:
self.encryption_options = {"useAwsOwnedKey": True}
self.engine_type = engine_type
self.engine_version = engine_version
self.host_instance_type = host_instance_type
self.ldap_server_metadata = ldap_server_metadata
self.logs = logs
if "general" not in self.logs:
self.logs["general"] = False
if "audit" not in self.logs:
if self.engine_type.upper() == "ACTIVEMQ":
self.logs["audit"] = False
self.maintenance_window_start_time = maintenance_window_start_time
if not self.maintenance_window_start_time:
self.maintenance_window_start_time = {
"dayOfWeek": "Sunday",
"timeOfDay": "00:00",
"timeZone": "UTC",
}
self.publicly_accessible = publicly_accessible
self.security_groups = security_groups
self.storage_type = storage_type
self.subnet_ids = subnet_ids
if not self.subnet_ids:
if self.deployment_mode == "CLUSTER_MULTI_AZ":
self.subnet_ids = [
"default-az1",
"default-az2",
"default-az3",
"default-az4",
]
elif self.deployment_mode == "ACTIVE_STANDBY_MULTI_AZ":
self.subnet_ids = ["active-subnet", "standby-subnet"]
else:
self.subnet_ids = ["default-subnet"]
self.users = dict()
for user in users:
self.create_user(
username=user["username"],
groups=user.get("groups", []),
console_access=user.get("consoleAccess", False),
)
if self.engine_type.upper() == "RABBITMQ":
self.configurations = None
else:
current_config = configuration or {
"id": f"c-{get_random_hex(6)}",
"revision": 1,
}
self.configurations = {
"current": current_config,
"history": [],
}
if self.engine_type.upper() == "RABBITMQ":
console_url = f"https://0000.mq.{region}.amazonaws.com"
endpoints = ["amqps://mockmq:5671"]
else:
console_url = f"https://0000.mq.{region}.amazonaws.com:8162"
endpoints = [
"ssl://mockmq:61617",
"amqp+ssl://mockmq:5671",
"stomp+ssl://mockmq:61614",
"mqtt+ssl://mockmq:8883",
"wss://mockmq:61619",
]
self.instances = [
{
"consoleURL": console_url,
"endpoints": endpoints,
"ipAddress": "192.168.0.1",
}
]
if deployment_mode == "ACTIVE_STANDBY_MULTI_AZ":
self.instances.append(
{
"consoleURL": console_url,
"endpoints": endpoints,
"ipAddress": "192.168.0.2",
}
)
def update(
self,
authentication_strategy,
auto_minor_version_upgrade,
configuration,
engine_version,
host_instance_type,
ldap_server_metadata,
logs,
maintenance_window_start_time,
security_groups,
):
if authentication_strategy:
self.authentication_strategy = authentication_strategy
if auto_minor_version_upgrade is not None:
self.auto_minor_version_upgrade = auto_minor_version_upgrade
if configuration:
self.configurations["history"].append(self.configurations["current"])
self.configurations["current"] = configuration
if engine_version:
self.engine_version = engine_version
if host_instance_type:
self.host_instance_type = host_instance_type
if ldap_server_metadata:
self.ldap_server_metadata = ldap_server_metadata
if logs:
self.logs = logs
if maintenance_window_start_time:
self.maintenance_window_start_time = maintenance_window_start_time
if security_groups:
self.security_groups = security_groups
def reboot(self):
pass
def create_user(self, username, console_access, groups):
user = User(self.id, username, console_access, groups)
self.users[username] = user
def update_user(self, username, console_access, groups):
user = self.get_user(username)
user.update(console_access, groups)
def get_user(self, username):
if username not in self.users:
raise UnknownUser(username)
return self.users[username]
def delete_user(self, username):
self.users.pop(username, None)
def list_users(self):
return self.users.values()
def summary(self):
return {
"brokerArn": self.arn,
"brokerId": self.id,
"brokerName": self.name,
"brokerState": self.state,
"created": self.created,
"deploymentMode": self.deployment_mode,
"engineType": self.engine_type,
"hostInstanceType": self.host_instance_type,
}
def to_json(self):
return {
"brokerId": self.id,
"brokerArn": self.arn,
"brokerName": self.name,
"brokerState": self.state,
"brokerInstances": self.instances,
"created": self.created,
"configurations": self.configurations,
"authenticationStrategy": self.authentication_strategy,
"autoMinorVersionUpgrade": self.auto_minor_version_upgrade,
"deploymentMode": self.deployment_mode,
"encryptionOptions": self.encryption_options,
"engineType": self.engine_type,
"engineVersion": self.engine_version,
"hostInstanceType": self.host_instance_type,
"ldapServerMetadata": self.ldap_server_metadata,
"logs": self.logs,
"maintenanceWindowStartTime": self.maintenance_window_start_time,
"publiclyAccessible": self.publicly_accessible,
"securityGroups": self.security_groups,
"storageType": self.storage_type,
"subnetIds": self.subnet_ids,
"users": [u.summary() for u in self.users.values()],
}
class MQBackend(BaseBackend):
"""
No EC2 integration exists yet - subnet ID's and security group values are not validated. Default values may not exist.
"""
def __init__(self, region_name=None):
self.region_name = region_name
self.brokers = dict()
self.configs = dict()
self.tagger = TaggingService()
def reset(self):
"""Re-initialize all attributes for this instance."""
region_name = self.region_name
self.__dict__ = {}
self.__init__(region_name)
def create_broker(
self,
authentication_strategy,
auto_minor_version_upgrade,
broker_name,
configuration,
creator_request_id,
deployment_mode,
encryption_options,
engine_type,
engine_version,
host_instance_type,
ldap_server_metadata,
logs,
maintenance_window_start_time,
publicly_accessible,
security_groups,
storage_type,
subnet_ids,
tags,
users,
):
broker = Broker(
name=broker_name,
region=self.region_name,
authentication_strategy=authentication_strategy,
auto_minor_version_upgrade=auto_minor_version_upgrade,
configuration=configuration,
deployment_mode=deployment_mode,
encryption_options=encryption_options,
engine_type=engine_type,
engine_version=engine_version,
host_instance_type=host_instance_type,
ldap_server_metadata=ldap_server_metadata,
logs=logs,
maintenance_window_start_time=maintenance_window_start_time,
publicly_accessible=publicly_accessible,
security_groups=security_groups,
storage_type=storage_type,
subnet_ids=subnet_ids,
users=users,
)
self.brokers[broker.id] = broker
self.create_tags(broker.arn, tags)
return broker.arn, broker.id
def delete_broker(self, broker_id):
del self.brokers[broker_id]
def describe_broker(self, broker_id):
if broker_id not in self.brokers:
raise UnknownBroker(broker_id)
return self.brokers[broker_id]
def reboot_broker(self, broker_id):
self.brokers[broker_id].reboot()
def list_brokers(self):
"""
Pagination is not yet implemented
"""
return self.brokers.values()
def create_user(self, broker_id, username, console_access, groups):
broker = self.describe_broker(broker_id)
broker.create_user(username, console_access, groups)
def update_user(self, broker_id, console_access, groups, username):
broker = self.describe_broker(broker_id)
broker.update_user(username, console_access, groups)
def describe_user(self, broker_id, username):
broker = self.describe_broker(broker_id)
return broker.get_user(username)
def delete_user(self, broker_id, username):
broker = self.describe_broker(broker_id)
broker.delete_user(username)
def list_users(self, broker_id):
broker = self.describe_broker(broker_id)
return broker.list_users()
def create_configuration(self, name, engine_type, engine_version, tags):
if engine_type.upper() == "RABBITMQ":
raise UnsupportedEngineType(engine_type)
if engine_type.upper() != "ACTIVEMQ":
raise UnknownEngineType(engine_type)
config = Configuration(
region=self.region_name,
name=name,
engine_type=engine_type,
engine_version=engine_version,
)
self.configs[config.id] = config
self.tagger.tag_resource(
config.arn, self.tagger.convert_dict_to_tags_input(tags)
)
return config
def update_configuration(self, config_id, data, description):
"""
No validation occurs on the provided XML. The authenticationStrategy may be changed depending on the provided configuration.
"""
config = self.configs[config_id]
config.update(data, description)
return config
def describe_configuration(self, config_id):
if config_id not in self.configs:
raise UnknownConfiguration(config_id)
return self.configs[config_id]
def describe_configuration_revision(self, config_id, revision_id):
config = self.configs[config_id]
return config.get_revision(revision_id)
def list_configurations(self):
"""
Pagination has not yet been implemented.
"""
return self.configs.values()
def create_tags(self, resource_arn, tags):
self.tagger.tag_resource(
resource_arn, self.tagger.convert_dict_to_tags_input(tags)
)
def list_tags(self, arn):
return self.tagger.get_tag_dict_for_resource(arn)
def delete_tags(self, resource_arn, tag_keys):
if not isinstance(tag_keys, list):
tag_keys = [tag_keys]
self.tagger.untag_resource_using_names(resource_arn, tag_keys)
def update_broker(
self,
authentication_strategy,
auto_minor_version_upgrade,
broker_id,
configuration,
engine_version,
host_instance_type,
ldap_server_metadata,
logs,
maintenance_window_start_time,
security_groups,
):
broker = self.describe_broker(broker_id)
broker.update(
authentication_strategy=authentication_strategy,
auto_minor_version_upgrade=auto_minor_version_upgrade,
configuration=configuration,
engine_version=engine_version,
host_instance_type=host_instance_type,
ldap_server_metadata=ldap_server_metadata,
logs=logs,
maintenance_window_start_time=maintenance_window_start_time,
security_groups=security_groups,
)
mq_backends = BackendDict(MQBackend, "mq")

281
moto/mq/responses.py Normal file
View File

@ -0,0 +1,281 @@
"""Handles incoming mq requests, invokes methods, returns responses."""
import json
from functools import wraps
from urllib.parse import unquote
from moto.core.responses import BaseResponse
from .exceptions import MQError
from .models import mq_backends
def error_handler(f):
@wraps(f)
def _wrapper(*args, **kwargs):
try:
return f(*args, **kwargs)
except MQError as e:
return e.code, e.get_headers(), e.get_body()
return _wrapper
class MQResponse(BaseResponse):
"""Handler for MQ requests and responses."""
@property
def mq_backend(self):
"""Return backend instance specific for this region."""
return mq_backends[self.region]
@error_handler
def broker(self, request, full_url, headers):
self.setup_class(request, full_url, headers)
if request.method == "GET":
return self.describe_broker()
if request.method == "DELETE":
return self.delete_broker()
if request.method == "PUT":
return self.update_broker()
def brokers(self, request, full_url, headers):
self.setup_class(request, full_url, headers)
if request.method == "POST":
return self.create_broker()
if request.method == "GET":
return self.list_brokers()
@error_handler
def configuration(self, request, full_url, headers):
self.setup_class(request, full_url, headers)
if request.method == "GET":
return self.describe_configuration()
if request.method == "PUT":
return self.update_configuration()
@error_handler
def configurations(self, request, full_url, headers):
self.setup_class(request, full_url, headers)
if request.method == "POST":
return self.create_configuration()
if request.method == "GET":
return self.list_configurations()
def configuration_revision(self, request, full_url, headers):
self.setup_class(request, full_url, headers)
if request.method == "GET":
return self.get_configuration_revision()
def tags(self, request, full_url, headers):
self.setup_class(request, full_url, headers)
if request.method == "POST":
return self.create_tags()
if request.method == "DELETE":
return self.delete_tags()
@error_handler
def user(self, request, full_url, headers):
self.setup_class(request, full_url, headers)
if request.method == "POST":
return self.create_user()
if request.method == "GET":
return self.describe_user()
if request.method == "PUT":
return self.update_user()
if request.method == "DELETE":
return self.delete_user()
def users(self, request, full_url, headers):
self.setup_class(request, full_url, headers)
if request.method == "GET":
return self.list_users()
def create_broker(self):
params = json.loads(self.body)
authentication_strategy = params.get("authenticationStrategy")
auto_minor_version_upgrade = params.get("autoMinorVersionUpgrade")
broker_name = params.get("brokerName")
configuration = params.get("configuration")
creator_request_id = params.get("creatorRequestId")
deployment_mode = params.get("deploymentMode")
encryption_options = params.get("encryptionOptions")
engine_type = params.get("engineType")
engine_version = params.get("engineVersion")
host_instance_type = params.get("hostInstanceType")
ldap_server_metadata = params.get("ldapServerMetadata")
logs = params.get("logs", {})
maintenance_window_start_time = params.get("maintenanceWindowStartTime")
publicly_accessible = params.get("publiclyAccessible")
security_groups = params.get("securityGroups")
storage_type = params.get("storageType")
subnet_ids = params.get("subnetIds", [])
tags = params.get("tags")
users = params.get("users", [])
broker_arn, broker_id = self.mq_backend.create_broker(
authentication_strategy=authentication_strategy,
auto_minor_version_upgrade=auto_minor_version_upgrade,
broker_name=broker_name,
configuration=configuration,
creator_request_id=creator_request_id,
deployment_mode=deployment_mode,
encryption_options=encryption_options,
engine_type=engine_type,
engine_version=engine_version,
host_instance_type=host_instance_type,
ldap_server_metadata=ldap_server_metadata,
logs=logs,
maintenance_window_start_time=maintenance_window_start_time,
publicly_accessible=publicly_accessible,
security_groups=security_groups,
storage_type=storage_type,
subnet_ids=subnet_ids,
tags=tags,
users=users,
)
# Lowercase members - boto3 will convert it into UpperCase
resp = {"brokerArn": broker_arn, "brokerId": broker_id}
return 200, {}, json.dumps(resp)
def update_broker(self):
params = json.loads(self.body)
broker_id = self.path.split("/")[-1]
authentication_strategy = params.get("authenticationStrategy")
auto_minor_version_upgrade = params.get("autoMinorVersionUpgrade")
configuration = params.get("configuration")
engine_version = params.get("engineVersion")
host_instance_type = params.get("hostInstanceType")
ldap_server_metadata = params.get("ldapServerMetadata")
logs = params.get("logs")
maintenance_window_start_time = params.get("maintenanceWindowStartTime")
security_groups = params.get("securityGroups")
self.mq_backend.update_broker(
authentication_strategy=authentication_strategy,
auto_minor_version_upgrade=auto_minor_version_upgrade,
broker_id=broker_id,
configuration=configuration,
engine_version=engine_version,
host_instance_type=host_instance_type,
ldap_server_metadata=ldap_server_metadata,
logs=logs,
maintenance_window_start_time=maintenance_window_start_time,
security_groups=security_groups,
)
return self.describe_broker()
def delete_broker(self):
broker_id = self.path.split("/")[-1]
self.mq_backend.delete_broker(broker_id=broker_id)
return 200, {}, json.dumps(dict(brokerId=broker_id))
def describe_broker(self):
broker_id = self.path.split("/")[-1]
broker = self.mq_backend.describe_broker(broker_id=broker_id)
resp = broker.to_json()
resp["tags"] = self.mq_backend.list_tags(broker.arn)
return 200, {}, json.dumps(resp)
def list_brokers(self):
brokers = self.mq_backend.list_brokers()
return 200, {}, json.dumps(dict(brokerSummaries=[b.summary() for b in brokers]))
def create_user(self):
params = json.loads(self.body)
broker_id = self.path.split("/")[-3]
username = self.path.split("/")[-1]
console_access = params.get("consoleAccess", False)
groups = params.get("groups", [])
self.mq_backend.create_user(broker_id, username, console_access, groups)
return 200, {}, "{}"
def update_user(self):
params = json.loads(self.body)
broker_id = self.path.split("/")[-3]
username = self.path.split("/")[-1]
console_access = params.get("consoleAccess", False)
groups = params.get("groups", [])
self.mq_backend.update_user(
broker_id=broker_id,
console_access=console_access,
groups=groups,
username=username,
)
return 200, {}, "{}"
def describe_user(self):
broker_id = self.path.split("/")[-3]
username = self.path.split("/")[-1]
user = self.mq_backend.describe_user(broker_id, username)
return 200, {}, json.dumps(user.to_json())
def delete_user(self):
broker_id = self.path.split("/")[-3]
username = self.path.split("/")[-1]
self.mq_backend.delete_user(broker_id, username)
return 200, {}, "{}"
def list_users(self):
broker_id = self.path.split("/")[-2]
users = self.mq_backend.list_users(broker_id=broker_id)
resp = {
"brokerId": broker_id,
"users": [{"username": u.username} for u in users],
}
return 200, {}, json.dumps(resp)
def create_configuration(self):
params = json.loads(self.body)
name = params.get("name")
engine_type = params.get("engineType")
engine_version = params.get("engineVersion")
tags = params.get("tags", {})
config = self.mq_backend.create_configuration(
name, engine_type, engine_version, tags
)
return 200, {}, json.dumps(config.to_json())
def describe_configuration(self):
config_id = self.path.split("/")[-1]
config = self.mq_backend.describe_configuration(config_id)
resp = config.to_json()
resp["tags"] = self.mq_backend.list_tags(config.arn)
return 200, {}, json.dumps(resp)
def list_configurations(self):
configs = self.mq_backend.list_configurations()
resp = {"configurations": [c.to_json() for c in configs]}
return 200, {}, json.dumps(resp)
def update_configuration(self):
config_id = self.path.split("/")[-1]
params = json.loads(self.body)
data = params.get("data")
description = params.get("description")
config = self.mq_backend.update_configuration(config_id, data, description)
return 200, {}, json.dumps(config.to_json())
def get_configuration_revision(self):
revision_id = self.path.split("/")[-1]
config_id = self.path.split("/")[-3]
revision = self.mq_backend.describe_configuration_revision(
config_id, revision_id
)
return 200, {}, json.dumps(revision.to_json())
def create_tags(self):
resource_arn = unquote(self.path.split("/")[-1])
tags = json.loads(self.body).get("tags", {})
self.mq_backend.create_tags(resource_arn, tags)
return 200, {}, "{}"
def delete_tags(self):
resource_arn = unquote(self.path.split("/")[-1])
tag_keys = self._get_param("tagKeys")
self.mq_backend.delete_tags(resource_arn, tag_keys)
return 200, {}, "{}"
def reboot(self, request, full_url, headers):
self.setup_class(request, full_url, headers)
if request.method == "POST":
broker_id = self.path.split("/")[-2]
self.mq_backend.reboot_broker(broker_id=broker_id)
return 200, {}, "{}"

22
moto/mq/urls.py Normal file
View File

@ -0,0 +1,22 @@
"""mq base URL and path."""
from .responses import MQResponse
url_bases = [
r"https?://mq\.(.+)\.amazonaws\.com",
]
response = MQResponse()
url_paths = {
"{0}/v1/brokers/(?P<broker_id>[^/]+)$": response.broker,
"{0}/v1/brokers/(?P<broker_id>[^/]+)/reboot$": response.reboot,
"{0}/v1/brokers/(?P<broker_id>[^/]+)/users$": response.users,
"{0}/v1/brokers/(?P<broker_id>[^/]+)/users/(?P<user_name>[^/]+)$": response.user,
"{0}/v1/brokers$": response.brokers,
"{0}/v1/configurations$": response.configurations,
"{0}/v1/configurations/(?P<config_id>[^/]+)$": response.configuration,
"{0}/v1/configurations/(?P<config_id>[^/]+)/revisions/(?P<revision_id>[^/]+)$": response.configuration_revision,
"{0}/v1/tags/(?P<resource_arn>[^/]+)$": response.tags,
}

View File

@ -68,6 +68,7 @@ TestAccAWSIAMUserPolicy
TestAccAWSIPRanges
TestAccAWSKmsAlias
TestAccAWSKmsSecretDataSource
TestAccAWSMq
TestAccAWSPartition
TestAccAWSProvider
TestAccAWSRedshiftServiceAccount

View File

427
tests/test_mq/test_mq.py Normal file
View File

@ -0,0 +1,427 @@
import boto3
import pytest
import sure # noqa # pylint: disable=unused-import
from botocore.exceptions import ClientError
from moto import mock_mq
# See our Development Tips on writing tests for hints on how to write good tests:
# http://docs.getmoto.org/en/latest/docs/contributing/development_tips/tests.html
@mock_mq
def test_create_broker_minimal():
client = boto3.client("mq", region_name="ap-southeast-1")
resp = client.create_broker(
AutoMinorVersionUpgrade=False,
BrokerName="testbroker",
DeploymentMode="dm",
EngineType="ACTIVEMQ",
EngineVersion="version",
HostInstanceType="hit",
PubliclyAccessible=True,
Users=[{"Username": "admin", "Password": "adm1n"}],
)
resp.should.have.key("BrokerId")
resp.should.have.key("BrokerArn").match("arn:aws")
@mock_mq
def test_create_with_tags():
client = boto3.client("mq", region_name="us-east-2")
broker_id = client.create_broker(
AutoMinorVersionUpgrade=False,
BrokerName="testbroker",
DeploymentMode="CLUSTER_MULTI_AZ",
EngineType="RabbitMQ",
EngineVersion="version",
HostInstanceType="hit",
PubliclyAccessible=True,
Tags={"key1": "val2", "key2": "val2"},
Users=[],
)["BrokerId"]
resp = client.describe_broker(BrokerId=broker_id)
resp.should.have.key("Tags").equals({"key1": "val2", "key2": "val2"})
@mock_mq
def test_create_with_multiple_users():
client = boto3.client("mq", region_name="us-east-2")
broker_id = client.create_broker(
AutoMinorVersionUpgrade=False,
BrokerName="testbroker",
DeploymentMode="CLUSTER_MULTI_AZ",
EngineType="RabbitMQ",
EngineVersion="version",
HostInstanceType="hit",
PubliclyAccessible=True,
Users=[
{
"ConsoleAccess": True,
"Groups": ["second", "first", "third"],
"Password": "SecondTestTest1234",
"Username": "SecondTest",
},
{
"ConsoleAccess": False,
"Groups": [],
"Password": "TestTest1234",
"Username": "Test",
},
],
)["BrokerId"]
user1 = client.describe_user(BrokerId=broker_id, Username="SecondTest")
user1.should.have.key("Username").equals("SecondTest")
user1.should.have.key("Groups").equals(["second", "first", "third"])
user1.should.have.key("ConsoleAccess").equals(True)
user2 = client.describe_user(BrokerId=broker_id, Username="Test")
user2.should.have.key("Username").equals("Test")
user2.should.have.key("Groups").equals([])
user2.should.have.key("ConsoleAccess").equals(False)
@mock_mq
def test_create_with_configuration():
client = boto3.client("mq", region_name="us-east-2")
broker_id = client.create_broker(
AutoMinorVersionUpgrade=False,
BrokerName="testbroker",
Configuration={"Id": "config_id_x", "Revision": 3},
DeploymentMode="CLUSTER_SINGLE",
EngineType="ActiveMQ",
EngineVersion="version",
HostInstanceType="hit",
PubliclyAccessible=True,
Tags={"key1": "val2", "key2": "val2"},
Users=[],
)["BrokerId"]
resp = client.describe_broker(BrokerId=broker_id)
resp.should.have.key("Configurations")
resp["Configurations"].should.have.key("Current").equals(
{"Id": "config_id_x", "Revision": 3}
)
@mock_mq
def test_update_with_configuration():
client = boto3.client("mq", region_name="us-east-2")
broker_id = client.create_broker(
AutoMinorVersionUpgrade=False,
BrokerName="testbroker",
Configuration={"Id": "config_id_x", "Revision": 1},
DeploymentMode="CLUSTER_SINGLE",
EngineType="ActiveMQ",
EngineVersion="version",
HostInstanceType="hit",
PubliclyAccessible=True,
Tags={"key1": "val2", "key2": "val2"},
Users=[],
)["BrokerId"]
client.update_broker(
BrokerId=broker_id, Configuration={"Id": "config_id_x", "Revision": 2}
)
resp = client.describe_broker(BrokerId=broker_id)
resp.should.have.key("Configurations")
resp["Configurations"].should.have.key("Current").equals(
{"Id": "config_id_x", "Revision": 2}
)
@mock_mq
def test_delete_broker():
client = boto3.client("mq", region_name="ap-southeast-1")
broker_id = client.create_broker(
AutoMinorVersionUpgrade=False,
BrokerName="testbroker",
DeploymentMode="dm",
EngineType="ACTIVEMQ",
EngineVersion="version",
HostInstanceType="hit",
PubliclyAccessible=True,
Users=[{"Username": "admin", "Password": "adm1n"}],
)["BrokerId"]
resp = client.delete_broker(BrokerId=broker_id)
resp.should.have.key("BrokerId").equals(broker_id)
client.list_brokers().should.have.key("BrokerSummaries").length_of(0)
@mock_mq
def test_describe_broker():
client = boto3.client("mq", region_name="us-east-2")
broker_id = client.create_broker(
AuthenticationStrategy="SIMPLE",
AutoMinorVersionUpgrade=False,
BrokerName="testbroker",
DeploymentMode="dm",
EncryptionOptions={"KmsKeyId": "kms-key", "UseAwsOwnedKey": False},
EngineType="ACTIVEMQ",
EngineVersion="version",
LdapServerMetadata={
"Hosts": ["host1"],
"RoleBase": "role_base_thingy",
"RoleSearchMatching": "rsm",
"ServiceAccountUsername": "sau",
"ServiceAccountPassword": "sap",
"UserBase": "ub",
"UserSearchMatching": "usm",
},
HostInstanceType="hit",
PubliclyAccessible=True,
SecurityGroups=["secgroup1"],
StorageType="efs",
SubnetIds=["s-id"],
Users=[{"Username": "admin", "Password": "adm1n"}],
)["BrokerId"]
resp = client.describe_broker(BrokerId=broker_id)
resp.should.have.key("BrokerId").equals(broker_id)
resp.should.have.key("BrokerArn").match("arn:aws")
resp.should.have.key("BrokerState").equals("RUNNING")
resp.should.have.key("Created")
resp.should.have.key("AuthenticationStrategy").equals("SIMPLE")
resp.should.have.key("AutoMinorVersionUpgrade").equals(False)
resp.should.have.key("BrokerName").equals("testbroker")
resp.should.have.key("DeploymentMode").equals("dm")
resp.should.have.key("EncryptionOptions").equals(
{"KmsKeyId": "kms-key", "UseAwsOwnedKey": False}
)
resp.should.have.key("EngineType").equals("ACTIVEMQ")
resp.should.have.key("EngineVersion").equals("version")
resp.should.have.key("HostInstanceType").equals("hit")
resp.should.have.key("LdapServerMetadata").equals(
{
"Hosts": ["host1"],
"RoleBase": "role_base_thingy",
"RoleSearchMatching": "rsm",
"ServiceAccountUsername": "sau",
"UserBase": "ub",
"UserSearchMatching": "usm",
}
)
resp.should.have.key("PubliclyAccessible").equals(True)
resp.should.have.key("SecurityGroups").equals(["secgroup1"])
resp.should.have.key("StorageType").equals("efs")
resp.should.have.key("SubnetIds").equals(["s-id"])
resp.should.have.key("Users").equals([{"Username": "admin"}])
@mock_mq
def test_describe_broker_with_defaults():
client = boto3.client("mq", region_name="us-east-2")
broker_id = client.create_broker(
AutoMinorVersionUpgrade=False,
BrokerName="testbroker",
DeploymentMode="dm",
EngineType="ACTIVEMQ",
EngineVersion="version",
HostInstanceType="hit",
PubliclyAccessible=True,
Users=[],
)["BrokerId"]
resp = client.describe_broker(BrokerId=broker_id)
resp.should.have.key("BrokerInstances").length_of(1)
resp.should.have.key("Configurations")
resp["Configurations"].should.have.key("Current")
resp["Configurations"].should.have.key("History").length_of(0)
resp["Configurations"].shouldnt.have.key("Pending")
resp.should.have.key("EncryptionOptions").equals({"UseAwsOwnedKey": True})
resp.should.have.key("MaintenanceWindowStartTime").equals(
{"DayOfWeek": "Sunday", "TimeOfDay": "00:00", "TimeZone": "UTC"}
)
resp.should.have.key("Logs").equals({"Audit": False, "General": False})
resp.should.have.key("SubnetIds").length_of(1)
@mock_mq
def test_describe_multiple_rabbits():
client = boto3.client("mq", region_name="us-east-2")
broker_id = client.create_broker(
AutoMinorVersionUpgrade=False,
BrokerName="testbroker",
DeploymentMode="CLUSTER_MULTI_AZ",
EngineType="RabbitMQ",
EngineVersion="version",
HostInstanceType="hit",
PubliclyAccessible=True,
Users=[],
)["BrokerId"]
resp = client.describe_broker(BrokerId=broker_id)
resp.should.have.key("BrokerInstances")
resp["BrokerInstances"][0]["ConsoleURL"].should.equal(
"https://0000.mq.us-east-2.amazonaws.com"
)
resp["BrokerInstances"][0]["Endpoints"].should.have.length_of(1)
resp.shouldnt.have.key("Configurations")
resp.should.have.key("Logs").equals({"General": False})
resp.should.have.key("SubnetIds").length_of(4)
@mock_mq
def test_describe_active_mq_with_standby():
client = boto3.client("mq", region_name="us-east-2")
broker_id = client.create_broker(
AutoMinorVersionUpgrade=False,
BrokerName="testbroker",
DeploymentMode="ACTIVE_STANDBY_MULTI_AZ",
EngineType="ActiveMQ",
EngineVersion="version",
HostInstanceType="hit",
PubliclyAccessible=True,
Users=[],
)["BrokerId"]
resp = client.describe_broker(BrokerId=broker_id)
# Instances and subnets in two regions - one active, one standby
resp.should.have.key("BrokerInstances").length_of(2)
resp.should.have.key("SubnetIds").length_of(2)
@mock_mq
def test_describe_broker_unknown():
client = boto3.client("mq", region_name="us-east-2")
with pytest.raises(ClientError) as exc:
client.describe_broker(BrokerId="unknown")
err = exc.value.response["Error"]
err["Code"].should.equal("NotFoundException")
err["Message"].should.equal(
"Can't find requested broker [unknown]. Make sure your broker exists."
)
@mock_mq
def test_list_brokers_empty():
client = boto3.client("mq", region_name="eu-west-1")
resp = client.list_brokers()
resp.should.have.key("BrokerSummaries").equals([])
@mock_mq
def test_list_brokers():
client = boto3.client("mq", region_name="eu-west-1")
broker_id = client.create_broker(
AutoMinorVersionUpgrade=False,
BrokerName="testbroker",
DeploymentMode="dm",
EngineType="ACTIVEMQ",
EngineVersion="version",
HostInstanceType="hit",
PubliclyAccessible=True,
Users=[{"Username": "admin", "Password": "adm1n"}],
)["BrokerId"]
resp = client.list_brokers()
resp.should.have.key("BrokerSummaries").length_of(1)
summary = resp["BrokerSummaries"][0]
summary.should.have.key("BrokerArn")
summary.should.have.key("BrokerId").equals(broker_id)
summary.should.have.key("BrokerName").equals("testbroker")
summary.should.have.key("BrokerState").equals("RUNNING")
summary.should.have.key("Created")
summary.should.have.key("DeploymentMode").equals("dm")
summary.should.have.key("EngineType").equals("ACTIVEMQ")
summary.should.have.key("HostInstanceType").equals("hit")
summary.shouldnt.have.key("Users")
@mock_mq
def test_update_broker_single_attribute():
client = boto3.client("mq", region_name="ap-southeast-1")
broker_id = client.create_broker(
AutoMinorVersionUpgrade=False,
BrokerName="testbroker",
DeploymentMode="dm",
EngineType="ACTIVEMQ",
EngineVersion="version",
HostInstanceType="hit",
PubliclyAccessible=True,
Users=[{"Username": "admin", "Password": "adm1n"}],
)["BrokerId"]
resp = client.update_broker(AutoMinorVersionUpgrade=True, BrokerId=broker_id)
# Changed
resp.should.have.key("AutoMinorVersionUpgrade").equals(True)
# Unchanged
resp.should.have.key("BrokerId").equals(broker_id)
resp.should.have.key("EngineVersion").equals("version")
@mock_mq
def test_update_broker_multiple_attributes():
client = boto3.client("mq", region_name="ap-southeast-1")
broker_id = client.create_broker(
AutoMinorVersionUpgrade=False,
BrokerName="testbroker",
DeploymentMode="dm",
EngineType="ACTIVEMQ",
EngineVersion="version",
HostInstanceType="hit",
PubliclyAccessible=True,
SecurityGroups=["sg-1"],
Users=[{"Username": "admin", "Password": "adm1n"}],
)["BrokerId"]
resp = client.update_broker(
AutoMinorVersionUpgrade=True,
BrokerId=broker_id,
Logs={"Audit": True, "General": True},
EngineVersion="version2",
SecurityGroups=["sg-1", "sg-2"],
)
# Changed
resp.should.have.key("AutoMinorVersionUpgrade").equals(True)
resp.should.have.key("Logs").equals({"Audit": True, "General": True})
resp.should.have.key("EngineVersion").equals("version2")
resp.should.have.key("SecurityGroups").equals(["sg-1", "sg-2"])
# Unchanged
resp.should.have.key("BrokerId").equals(broker_id)
@mock_mq
def test_reboot_broker():
client = boto3.client("mq", region_name="ap-southeast-1")
broker_id = client.create_broker(
AutoMinorVersionUpgrade=False,
BrokerName="testbroker",
DeploymentMode="dm",
EngineType="ACTIVEMQ",
EngineVersion="version",
HostInstanceType="hit",
PubliclyAccessible=True,
Users=[{"Username": "admin", "Password": "adm1n"}],
)["BrokerId"]
client.reboot_broker(BrokerId=broker_id)
# Noop - nothing to assert or verify
pass

View File

@ -0,0 +1,210 @@
"""Unit tests for mq-supported APIs."""
import base64
import boto3
import pytest
import sure # noqa # pylint: disable=unused-import
from botocore.exceptions import ClientError
from moto import mock_mq
from moto.core import ACCOUNT_ID
# See our Development Tips on writing tests for hints on how to write good tests:
# http://docs.getmoto.org/en/latest/docs/contributing/development_tips/tests.html
@mock_mq
def test_create_configuration_minimal():
client = boto3.client("mq", region_name="ap-southeast-1")
resp = client.create_configuration(
EngineType="ACTIVEMQ", EngineVersion="rabbit1", Name="myconfig"
)
resp.should.have.key("Id").match("^c-")
resp.should.have.key("Arn").equals(
f"arn:aws:mq:ap-southeast-1:{ACCOUNT_ID}:configuration:{resp['Id']}"
)
resp.should.have.key("AuthenticationStrategy").equals("simple")
resp.should.have.key("Created")
resp.should.have.key("Name").equals("myconfig")
resp.should.have.key("LatestRevision")
revision = resp["LatestRevision"]
revision.should.have.key("Created")
revision.should.have.key("Description")
revision.should.have.key("Revision").equals(1)
@mock_mq
def test_create_configuration_for_rabbitmq():
client = boto3.client("mq", region_name="us-east-1")
with pytest.raises(ClientError) as exc:
client.create_configuration(
EngineType="RABBITMQ", EngineVersion="rabbit1", Name="myconfig"
)
err = exc.value.response["Error"]
err["Code"].should.equal("BadRequestException")
err["Message"].should.equal(
"Broker engine type [RABBITMQ] does not support configuration."
)
@mock_mq
def test_create_configuration_for_unknown_engine():
client = boto3.client("mq", region_name="us-east-1")
with pytest.raises(ClientError) as exc:
client.create_configuration(
EngineType="unknown", EngineVersion="rabbit1", Name="myconfig"
)
err = exc.value.response["Error"]
err["Code"].should.equal("BadRequestException")
err["Message"].should.equal(
"Broker engine type [unknown] is invalid. Valid values are: [ACTIVEMQ]"
)
@mock_mq
def test_describe_configuration():
client = boto3.client("mq", region_name="eu-north-1")
config_id = client.create_configuration(
EngineType="ACTIVEMQ", EngineVersion="active2", Name="myconfig"
)["Id"]
resp = client.describe_configuration(ConfigurationId=config_id)
resp.should.have.key("Id").match("^c-")
resp.should.have.key("Arn").equals(
f"arn:aws:mq:eu-north-1:{ACCOUNT_ID}:configuration:{resp['Id']}"
)
resp.should.have.key("AuthenticationStrategy").equals("simple")
resp.should.have.key("Created")
resp.should.have.key("Name").equals("myconfig")
resp.should.have.key("LatestRevision")
revision = resp["LatestRevision"]
revision.should.have.key("Created")
revision.should.have.key("Description")
revision.should.have.key("Revision").equals(1)
@mock_mq
def test_describe_configuration_revision():
client = boto3.client("mq", region_name="eu-north-1")
config_id = client.create_configuration(
EngineType="ActiveMQ", EngineVersion="5.16.3", Name="myconfig"
)["Id"]
resp = client.describe_configuration_revision(
ConfigurationId=config_id, ConfigurationRevision="1"
)
resp.should.have.key("ConfigurationId").equals(config_id)
resp.should.have.key("Created")
resp.should.have.key("Description").equals(
"Auto-generated default for myconfig on ActiveMQ 5.16.3"
)
resp.should.have.key("Data")
@mock_mq
def test_describe_configuration_unknown():
client = boto3.client("mq", region_name="us-east-2")
with pytest.raises(ClientError) as exc:
client.describe_configuration(ConfigurationId="c-unknown")
err = exc.value.response["Error"]
err["Code"].should.equal("NotFoundException")
err["Message"].should.equal(
"Can't find requested configuration [c-unknown]. Make sure your configuration exists."
)
@mock_mq
def test_list_configurations_empty():
client = boto3.client("mq", region_name="us-east-2")
resp = client.list_configurations()
resp.should.have.key("Configurations").equals([])
@mock_mq
def test_list_configurations():
client = boto3.client("mq", region_name="ap-southeast-1")
config_id = client.create_configuration(
EngineType="ACTIVEMQ", EngineVersion="active1", Name="myconfig"
)["Id"]
resp = client.list_configurations()
resp.should.have.key("Configurations").length_of(1)
config = resp["Configurations"][0]
config.should.have.key("Arn").match("arn:aws")
config.should.have.key("Created")
config.should.have.key("Id").equals(config_id)
config.should.have.key("Name").equals("myconfig")
config.should.have.key("EngineType").equals("ACTIVEMQ")
config.should.have.key("EngineVersion").equals("active1")
@mock_mq
def test_update_configuration():
client = boto3.client("mq", region_name="ap-southeast-1")
config_id = client.create_configuration(
EngineType="ACTIVEMQ", EngineVersion="rabbit1", Name="myconfig"
)["Id"]
resp = client.update_configuration(
ConfigurationId=config_id,
Data="base64encodedxmlconfig",
Description="updated config",
)
resp.should.have.key("Arn").match("arn:aws:mq")
resp.should.have.key("Created")
resp.should.have.key("Id")
resp.should.have.key("Name").equals("myconfig")
resp.should.have.key("LatestRevision")
revision = resp["LatestRevision"]
revision.should.have.key("Created")
revision.should.have.key("Description").equals("updated config")
revision.should.have.key("Revision").equals(2)
@mock_mq
def test_update_configuration_to_ldap():
client = boto3.client("mq", region_name="ap-southeast-1")
config_id = client.create_configuration(
EngineType="ACTIVEMQ", EngineVersion="rabbit1", Name="myconfig"
)["Id"]
ldap_config = """<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<broker xmlns="http://activemq.apache.org/schema/core">
<plugins>
<authorizationPlugin>
<map>
<cachedLDAPAuthorizationMap legacyGroupMapping="false" queueSearchBase="ou=Queue,ou=Destination,ou=ActiveMQ,dc=example,dc=org" refreshInterval="0" tempSearchBase="ou=Temp,ou=Destination,ou=ActiveMQ,dc=example,dc=org" topicSearchBase="ou=Topic,ou=Destination,ou=ActiveMQ,dc=example,dc=org"/>
</map>
</authorizationPlugin>
<forcePersistencyModeBrokerPlugin persistenceFlag="true"/>
<statisticsBrokerPlugin/>
<timeStampingBrokerPlugin ttlCeiling="86400000" zeroExpirationOverride="86400000"/>
</plugins>
</broker>
"""
client.update_configuration(
ConfigurationId=config_id,
Data=base64.b64encode(ldap_config.encode("utf-8")).decode("utf-8"),
Description="update config to use LDAP authorization",
)
resp = client.describe_configuration(ConfigurationId=config_id)
resp.should.have.key("AuthenticationStrategy").equals("ldap")

View File

@ -0,0 +1,109 @@
import boto3
import sure # noqa # pylint: disable=unused-import
from moto import mock_mq
@mock_mq
def test_create_broker_with_tags():
client = boto3.client("mq", region_name="us-east-2")
broker_id = client.create_broker(
AutoMinorVersionUpgrade=False,
BrokerName="testbroker",
DeploymentMode="CLUSTER_MULTI_AZ",
EngineType="RabbitMQ",
EngineVersion="version",
HostInstanceType="hit",
PubliclyAccessible=True,
Tags={"key1": "val2", "key2": "val2"},
Users=[],
)["BrokerId"]
resp = client.describe_broker(BrokerId=broker_id)
resp.should.have.key("Tags").equals({"key1": "val2", "key2": "val2"})
@mock_mq
def test_create_tags():
client = boto3.client("mq", region_name="us-east-2")
resp = client.create_broker(
AutoMinorVersionUpgrade=False,
BrokerName="testbroker",
DeploymentMode="CLUSTER_MULTI_AZ",
EngineType="RabbitMQ",
EngineVersion="version",
HostInstanceType="hit",
PubliclyAccessible=True,
Users=[],
)
broker_arn = resp["BrokerArn"]
broker_id = resp["BrokerId"]
client.create_tags(ResourceArn=broker_arn, Tags={"key1": "val2", "key2": "val2"})
resp = client.describe_broker(BrokerId=broker_id)
resp.should.have.key("Tags").equals({"key1": "val2", "key2": "val2"})
@mock_mq
def test_delete_tags():
client = boto3.client("mq", region_name="us-east-2")
resp = client.create_broker(
AutoMinorVersionUpgrade=False,
BrokerName="testbroker",
DeploymentMode="CLUSTER_MULTI_AZ",
EngineType="RabbitMQ",
EngineVersion="version",
HostInstanceType="hit",
PubliclyAccessible=True,
Users=[],
)
broker_arn = resp["BrokerArn"]
broker_id = resp["BrokerId"]
client.create_tags(ResourceArn=broker_arn, Tags={"key1": "val2", "key2": "val2"})
client.delete_tags(ResourceArn=broker_arn, TagKeys=["key1"])
resp = client.describe_broker(BrokerId=broker_id)
resp.should.have.key("Tags").equals({"key2": "val2"})
@mock_mq
def test_create_configuration_with_tags():
client = boto3.client("mq", region_name="ap-southeast-1")
resp = client.create_configuration(
EngineType="ACTIVEMQ",
EngineVersion="rabbit1",
Name="myconfig",
Tags={"key1": "val1", "key2": "val2"},
)
# The CreateConfiguration call does not return tags
resp.shouldnt.have.key("Tags")
# Only when describing will they be returned
resp = client.describe_configuration(ConfigurationId=resp["Id"])
resp.should.have.key("Tags").equals({"key1": "val1", "key2": "val2"})
@mock_mq
def test_add_tags_to_configuration():
client = boto3.client("mq", region_name="ap-southeast-1")
resp = client.create_configuration(
EngineType="ACTIVEMQ",
EngineVersion="rabbit1",
Name="myconfig",
Tags={"key1": "val1", "key2": "val2"},
)
client.create_tags(ResourceArn=resp["Arn"], Tags={"key1": "val1", "key2": "val2"})
# Only when describing will they be returned
resp = client.describe_configuration(ConfigurationId=resp["Id"])
resp.should.have.key("Tags").equals({"key1": "val1", "key2": "val2"})

View File

@ -0,0 +1,172 @@
import boto3
import pytest
import sure # noqa # pylint: disable=unused-import
from botocore.exceptions import ClientError
from moto import mock_mq
@mock_mq
def test_create_user():
client = boto3.client("mq", region_name="us-east-1")
broker_id = client.create_broker(
AutoMinorVersionUpgrade=False,
BrokerName="testbroker",
DeploymentMode="dm",
EngineType="ACTIVEMQ",
EngineVersion="version",
HostInstanceType="hit",
PubliclyAccessible=True,
Users=[],
)["BrokerId"]
client.create_user(BrokerId=broker_id, Username="admin", Password="adm1n")
resp = client.describe_broker(BrokerId=broker_id)
resp.should.have.key("Users").equals([{"Username": "admin"}])
@mock_mq
def test_describe_user():
client = boto3.client("mq", region_name="us-east-1")
broker_id = client.create_broker(
AutoMinorVersionUpgrade=False,
BrokerName="testbroker",
DeploymentMode="dm",
EngineType="ACTIVEMQ",
EngineVersion="version",
HostInstanceType="hit",
PubliclyAccessible=True,
Users=[],
)["BrokerId"]
client.create_user(
BrokerId=broker_id,
Username="admin",
Password="adm1n",
ConsoleAccess=True,
Groups=["group1", "group2"],
)
resp = client.describe_user(BrokerId=broker_id, Username="admin")
resp.should.have.key("BrokerId").equals(broker_id)
resp.should.have.key("ConsoleAccess").equals(True)
resp.should.have.key("Groups").equals(["group1", "group2"])
resp.should.have.key("Username").equals("admin")
@mock_mq
def test_describe_user_unknown():
client = boto3.client("mq", region_name="us-east-2")
broker_id = client.create_broker(
AutoMinorVersionUpgrade=False,
BrokerName="testbroker",
DeploymentMode="dm",
EngineType="ACTIVEMQ",
EngineVersion="version",
HostInstanceType="hit",
PubliclyAccessible=True,
Users=[],
)["BrokerId"]
with pytest.raises(ClientError) as exc:
client.describe_user(BrokerId=broker_id, Username="unknown")
err = exc.value.response["Error"]
err["Code"].should.equal("NotFoundException")
err["Message"].should.equal(
"Can't find requested user [unknown]. Make sure your user exists."
)
@mock_mq
def test_list_users_empty():
client = boto3.client("mq", region_name="us-east-1")
broker_id = client.create_broker(
AutoMinorVersionUpgrade=False,
BrokerName="testbroker",
DeploymentMode="dm",
EngineType="ACTIVEMQ",
EngineVersion="version",
HostInstanceType="hit",
PubliclyAccessible=True,
Users=[],
)["BrokerId"]
resp = client.list_users(BrokerId=broker_id)
resp.should.have.key("BrokerId").equals(broker_id)
resp.should.have.key("Users").equals([])
@mock_mq
def test_list_users():
client = boto3.client("mq", region_name="us-east-1")
broker_id = client.create_broker(
AutoMinorVersionUpgrade=False,
BrokerName="testbroker",
DeploymentMode="dm",
EngineType="ACTIVEMQ",
EngineVersion="version",
HostInstanceType="hit",
PubliclyAccessible=True,
Users=[{"Username": "admin", "Password": "adm1n"}],
)["BrokerId"]
client.create_user(BrokerId=broker_id, Username="user1", Password="us3r1")
resp = client.list_users(BrokerId=broker_id)
resp.should.have.key("BrokerId").equals(broker_id)
resp.should.have.key("Users").length_of(2)
resp["Users"].should.contain({"Username": "admin"})
resp["Users"].should.contain({"Username": "user1"})
@mock_mq
def test_update_user():
client = boto3.client("mq", region_name="us-east-2")
broker_id = client.create_broker(
AutoMinorVersionUpgrade=False,
BrokerName="testbroker",
DeploymentMode="dm",
EngineType="ACTIVEMQ",
EngineVersion="version",
HostInstanceType="hit",
PubliclyAccessible=True,
Users=[{"Username": "admin", "Password": "adm1n"}],
)["BrokerId"]
client.update_user(BrokerId=broker_id, Username="admin", Groups=["administrators"])
resp = client.describe_user(BrokerId=broker_id, Username="admin")
resp.should.have.key("BrokerId").equals(broker_id)
resp.should.have.key("Groups").equals(["administrators"])
resp.should.have.key("Username").equals("admin")
@mock_mq
def test_delete_user():
client = boto3.client("mq", region_name="us-east-1")
broker_id = client.create_broker(
AutoMinorVersionUpgrade=False,
BrokerName="testbroker",
DeploymentMode="dm",
EngineType="ACTIVEMQ",
EngineVersion="version",
HostInstanceType="hit",
PubliclyAccessible=True,
Users=[{"Username": "admin", "Password": "adm1n"}],
)["BrokerId"]
client.create_user(BrokerId=broker_id, Username="user1", Password="us3r1")
client.delete_user(BrokerId=broker_id, Username="admin")
resp = client.list_users(BrokerId=broker_id)
resp.should.have.key("BrokerId").equals(broker_id)
resp.should.have.key("Users").length_of(1)
resp["Users"].should.contain({"Username": "user1"})

View File

@ -0,0 +1,15 @@
import json
import sure # noqa # pylint: disable=unused-import
from moto import mock_mq
import moto.server as server
@mock_mq
def test_mq_list():
backend = server.create_backend_app("mq")
test_client = backend.test_client()
resp = test_client.get("/v1/brokers")
resp.status_code.should.equal(200)
json.loads(resp.data).should.equal({"brokerSummaries": []})