diff --git a/IMPLEMENTATION_COVERAGE.md b/IMPLEMENTATION_COVERAGE.md
index 30c825839..2baf7eb53 100644
--- a/IMPLEMENTATION_COVERAGE.md
+++ b/IMPLEMENTATION_COVERAGE.md
@@ -3587,6 +3587,34 @@
- [X] put_object
+## mq
+
+86% implemented
+
+- [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
+
+
## opsworks
12% implemented
@@ -5372,7 +5400,6 @@
- migrationhub-config
- migrationhubstrategy
- mobile
-- mq
- mturk
- mwaa
- neptune
diff --git a/docs/docs/services/mq.rst b/docs/docs/services/mq.rst
new file mode 100644
index 000000000..d863fd103
--- /dev/null
+++ b/docs/docs/services/mq.rst
@@ -0,0 +1,64 @@
+.. _implementedservice_mq:
+
+.. |start-h3| raw:: html
+
+
+
+.. |end-h3| raw:: html
+
+
+
+==
+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
+
diff --git a/moto/__init__.py b/moto/__init__.py
index 6458eafec..a61a6318c 100644
--- a/moto/__init__.py
+++ b/moto/__init__.py
@@ -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")
diff --git a/moto/backend_index.py b/moto/backend_index.py
index 66a13001d..b14525e38 100644
--- a/moto/backend_index.py
+++ b/moto/backend_index.py
@@ -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")),
diff --git a/moto/mq/__init__.py b/moto/mq/__init__.py
new file mode 100644
index 000000000..cde94761c
--- /dev/null
+++ b/moto/mq/__init__.py
@@ -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)
diff --git a/moto/mq/configuration.py b/moto/mq/configuration.py
new file mode 100644
index 000000000..6797ed2ee
--- /dev/null
+++ b/moto/mq/configuration.py
@@ -0,0 +1,183 @@
+DEFAULT_CONFIGURATION_DATA = """
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+"""
diff --git a/moto/mq/exceptions.py b/moto/mq/exceptions.py
new file mode 100644
index 000000000..63a97fbe7
--- /dev/null
+++ b/moto/mq/exceptions.py
@@ -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)
diff --git a/moto/mq/models.py b/moto/mq/models.py
new file mode 100644
index 000000000..530b90bfc
--- /dev/null
+++ b/moto/mq/models.py
@@ -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")
diff --git a/moto/mq/responses.py b/moto/mq/responses.py
new file mode 100644
index 000000000..06276f55f
--- /dev/null
+++ b/moto/mq/responses.py
@@ -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, {}, "{}"
diff --git a/moto/mq/urls.py b/moto/mq/urls.py
new file mode 100644
index 000000000..26cc6cd8d
--- /dev/null
+++ b/moto/mq/urls.py
@@ -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[^/]+)$": response.broker,
+ "{0}/v1/brokers/(?P[^/]+)/reboot$": response.reboot,
+ "{0}/v1/brokers/(?P[^/]+)/users$": response.users,
+ "{0}/v1/brokers/(?P[^/]+)/users/(?P[^/]+)$": response.user,
+ "{0}/v1/brokers$": response.brokers,
+ "{0}/v1/configurations$": response.configurations,
+ "{0}/v1/configurations/(?P[^/]+)$": response.configuration,
+ "{0}/v1/configurations/(?P[^/]+)/revisions/(?P[^/]+)$": response.configuration_revision,
+ "{0}/v1/tags/(?P[^/]+)$": response.tags,
+}
diff --git a/tests/terraform-tests.success.txt b/tests/terraform-tests.success.txt
index ce6bbfd9a..a9b884435 100644
--- a/tests/terraform-tests.success.txt
+++ b/tests/terraform-tests.success.txt
@@ -68,6 +68,7 @@ TestAccAWSIAMUserPolicy
TestAccAWSIPRanges
TestAccAWSKmsAlias
TestAccAWSKmsSecretDataSource
+TestAccAWSMq
TestAccAWSPartition
TestAccAWSProvider
TestAccAWSRedshiftServiceAccount
diff --git a/tests/test_mq/__init__.py b/tests/test_mq/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/tests/test_mq/test_mq.py b/tests/test_mq/test_mq.py
new file mode 100644
index 000000000..370cc74d8
--- /dev/null
+++ b/tests/test_mq/test_mq.py
@@ -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
diff --git a/tests/test_mq/test_mq_configuration.py b/tests/test_mq/test_mq_configuration.py
new file mode 100644
index 000000000..cefbe5fac
--- /dev/null
+++ b/tests/test_mq/test_mq_configuration.py
@@ -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 = """
+
+
+
+
+
+
+
+
+
+
+"""
+
+ 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")
diff --git a/tests/test_mq/test_mq_tags.py b/tests/test_mq/test_mq_tags.py
new file mode 100644
index 000000000..ccf109834
--- /dev/null
+++ b/tests/test_mq/test_mq_tags.py
@@ -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"})
diff --git a/tests/test_mq/test_mq_users.py b/tests/test_mq/test_mq_users.py
new file mode 100644
index 000000000..4c3f04099
--- /dev/null
+++ b/tests/test_mq/test_mq_users.py
@@ -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"})
diff --git a/tests/test_mq/test_server.py b/tests/test_mq/test_server.py
new file mode 100644
index 000000000..b667e1145
--- /dev/null
+++ b/tests/test_mq/test_server.py
@@ -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": []})