diff --git a/IMPLEMENTATION_COVERAGE.md b/IMPLEMENTATION_COVERAGE.md
index 946ad7970..06de16edc 100644
--- a/IMPLEMENTATION_COVERAGE.md
+++ b/IMPLEMENTATION_COVERAGE.md
@@ -4810,6 +4810,38 @@
- [ ] validate_resource_policy
+## servicediscovery
+
+61% implemented
+
+- [X] create_http_namespace
+- [X] create_private_dns_namespace
+- [X] create_public_dns_namespace
+- [X] create_service
+- [X] delete_namespace
+- [X] delete_service
+- [ ] deregister_instance
+- [ ] discover_instances
+- [ ] get_instance
+- [ ] get_instances_health_status
+- [X] get_namespace
+- [X] get_operation
+- [X] get_service
+- [ ] list_instances
+- [X] list_namespaces
+- [X] list_operations
+- [X] list_services
+- [X] list_tags_for_resource
+- [ ] register_instance
+- [X] tag_resource
+- [X] untag_resource
+- [ ] update_http_namespace
+- [ ] update_instance_custom_health_status
+- [ ] update_private_dns_namespace
+- [ ] update_public_dns_namespace
+- [X] update_service
+
+
## ses
32% implemented
@@ -5544,7 +5576,6 @@
- service-quotas
- servicecatalog
- servicecatalog-appregistry
-- servicediscovery
- sesv2
- shield
- signer
diff --git a/docs/docs/services/servicediscovery.rst b/docs/docs/services/servicediscovery.rst
new file mode 100644
index 000000000..196dcc94a
--- /dev/null
+++ b/docs/docs/services/servicediscovery.rst
@@ -0,0 +1,68 @@
+.. _implementedservice_servicediscovery:
+
+.. |start-h3| raw:: html
+
+
+
+.. |end-h3| raw:: html
+
+
+
+================
+servicediscovery
+================
+
+.. autoclass:: moto.servicediscovery.models.ServiceDiscoveryBackend
+
+|start-h3| Example usage |end-h3|
+
+.. sourcecode:: python
+
+ @mock_servicediscovery
+ def test_servicediscovery_behaviour:
+ boto3.client("servicediscovery")
+ ...
+
+
+
+|start-h3| Implemented features for this service |end-h3|
+
+- [X] create_http_namespace
+- [X] create_private_dns_namespace
+- [X] create_public_dns_namespace
+- [X] create_service
+- [X] delete_namespace
+- [X] delete_service
+- [ ] deregister_instance
+- [ ] discover_instances
+- [ ] get_instance
+- [ ] get_instances_health_status
+- [X] get_namespace
+- [X] get_operation
+- [X] get_service
+- [ ] list_instances
+- [X] list_namespaces
+
+ Pagination or the Filters-parameter is not yet implemented
+
+
+- [X] list_operations
+
+ Pagination or the Filters-argument is not yet implemented
+
+
+- [X] list_services
+
+ Pagination or the Filters-argument is not yet implemented
+
+
+- [X] list_tags_for_resource
+- [ ] register_instance
+- [X] tag_resource
+- [X] untag_resource
+- [ ] update_http_namespace
+- [ ] update_instance_custom_health_status
+- [ ] update_private_dns_namespace
+- [ ] update_public_dns_namespace
+- [X] update_service
+
diff --git a/moto/__init__.py b/moto/__init__.py
index 3178dc64c..00036dbaa 100644
--- a/moto/__init__.py
+++ b/moto/__init__.py
@@ -130,6 +130,7 @@ mock_sagemaker = lazy_load(".sagemaker", "mock_sagemaker")
mock_sdb = lazy_load(".sdb", "mock_sdb")
mock_secretsmanager = lazy_load(".secretsmanager", "mock_secretsmanager")
mock_ses = lazy_load(".ses", "mock_ses")
+mock_servicediscovery = lazy_load(".servicediscovery", "mock_servicediscovery")
mock_sns = lazy_load(".sns", "mock_sns")
mock_sqs = lazy_load(".sqs", "mock_sqs")
mock_ssm = lazy_load(".ssm", "mock_ssm")
diff --git a/moto/backend_index.py b/moto/backend_index.py
index f58ad44d5..7a60336e3 100644
--- a/moto/backend_index.py
+++ b/moto/backend_index.py
@@ -124,6 +124,10 @@ backend_url_patterns = [
("sagemaker", re.compile("https?://api.sagemaker\\.(.+)\\.amazonaws.com")),
("sdb", re.compile("https?://sdb\\.(.+)\\.amazonaws\\.com")),
("secretsmanager", re.compile("https?://secretsmanager\\.(.+)\\.amazonaws\\.com")),
+ (
+ "servicediscovery",
+ re.compile("https?://servicediscovery\\.(.+)\\.amazonaws\\.com"),
+ ),
("ses", re.compile("https?://email\\.(.+)\\.amazonaws\\.com")),
("ses", re.compile("https?://ses\\.(.+)\\.amazonaws\\.com")),
("sns", re.compile("https?://sns\\.(.+)\\.amazonaws\\.com")),
diff --git a/moto/servicediscovery/__init__.py b/moto/servicediscovery/__init__.py
new file mode 100644
index 000000000..983db1ab4
--- /dev/null
+++ b/moto/servicediscovery/__init__.py
@@ -0,0 +1,5 @@
+"""servicediscovery module initialization; sets value for base decorator."""
+from .models import servicediscovery_backends
+from ..core.models import base_decorator
+
+mock_servicediscovery = base_decorator(servicediscovery_backends)
diff --git a/moto/servicediscovery/exceptions.py b/moto/servicediscovery/exceptions.py
new file mode 100644
index 000000000..f816fcc2c
--- /dev/null
+++ b/moto/servicediscovery/exceptions.py
@@ -0,0 +1,22 @@
+"""Exceptions raised by the servicediscovery service."""
+from moto.core.exceptions import JsonRESTError
+
+
+class OperationNotFound(JsonRESTError):
+ def __init__(self):
+ super().__init__("OperationNotFound", "")
+
+
+class NamespaceNotFound(JsonRESTError):
+ def __init__(self, ns_id):
+ super().__init__("NamespaceNotFound", f"{ns_id}")
+
+
+class ServiceNotFound(JsonRESTError):
+ def __init__(self, ns_id):
+ super().__init__("ServiceNotFound", f"{ns_id}")
+
+
+class ConflictingDomainExists(JsonRESTError):
+ def __init__(self, vpc_id):
+ super().__init__("ConflictingDomainExists", f"{vpc_id}")
diff --git a/moto/servicediscovery/models.py b/moto/servicediscovery/models.py
new file mode 100644
index 000000000..c76c459db
--- /dev/null
+++ b/moto/servicediscovery/models.py
@@ -0,0 +1,330 @@
+import random
+import string
+
+from moto.core import ACCOUNT_ID, BaseBackend, BaseModel
+from moto.core.utils import BackendDict, unix_time
+from moto.utilities.tagging_service import TaggingService
+
+from .exceptions import (
+ ConflictingDomainExists,
+ NamespaceNotFound,
+ OperationNotFound,
+ ServiceNotFound,
+)
+
+
+def random_id(size):
+ return "".join(
+ [random.choice(string.ascii_lowercase + string.digits) for _ in range(size)]
+ )
+
+
+class Namespace(BaseModel):
+ def __init__(
+ self,
+ region,
+ name,
+ ns_type,
+ creator_request_id,
+ description,
+ dns_properties,
+ http_properties,
+ vpc=None,
+ ):
+ super().__init__()
+ self.id = f"ns-{random_id(20)}"
+ self.arn = f"arn:aws:servicediscovery:{region}:{ACCOUNT_ID}:namespace/{self.id}"
+ self.name = name
+ self.type = ns_type
+ self.creator_request_id = creator_request_id
+ self.description = description
+ self.dns_properties = dns_properties
+ self.http_properties = http_properties
+ self.vpc = vpc
+ self.created = unix_time()
+ self.updated = unix_time()
+
+ def to_json(self):
+ return {
+ "Arn": self.arn,
+ "Id": self.id,
+ "Name": self.name,
+ "Description": self.description,
+ "Type": self.type,
+ "Properties": {
+ "DnsProperties": self.dns_properties,
+ "HttpProperties": self.http_properties,
+ },
+ "CreateDate": self.created,
+ "UpdateDate": self.updated,
+ "CreatorRequestId": self.creator_request_id,
+ }
+
+
+class Service(BaseModel):
+ def __init__(
+ self,
+ region,
+ name,
+ namespace_id,
+ description,
+ creator_request_id,
+ dns_config,
+ health_check_config,
+ health_check_custom_config,
+ service_type,
+ ):
+ super().__init__()
+ self.id = f"srv-{random_id(8)}"
+ self.arn = f"arn:aws:servicediscovery:{region}:{ACCOUNT_ID}:service/{self.id}"
+ self.name = name
+ self.namespace_id = namespace_id
+ self.description = description
+ self.creator_request_id = creator_request_id
+ self.dns_config = dns_config
+ self.health_check_config = health_check_config
+ self.health_check_custom_config = health_check_custom_config
+ self.service_type = service_type
+ self.created = unix_time()
+
+ def update(self, details):
+ if "Description" in details:
+ self.description = details["Description"]
+ if "DnsConfig" in details:
+ if self.dns_config is None:
+ self.dns_config = {}
+ self.dns_config["DnsRecords"] = details["DnsConfig"]["DnsRecords"]
+ else:
+ # From the docs:
+ # If you omit any existing DnsRecords or HealthCheckConfig configurations from an UpdateService request,
+ # the configurations are deleted from the service.
+ self.dns_config = None
+ if "HealthCheckConfig" in details:
+ self.health_check_config = details["HealthCheckConfig"]
+
+ def to_json(self):
+ return {
+ "Arn": self.arn,
+ "Id": self.id,
+ "Name": self.name,
+ "NamespaceId": self.namespace_id,
+ "CreateDate": self.created,
+ "Description": self.description,
+ "CreatorRequestId": self.creator_request_id,
+ "DnsConfig": self.dns_config,
+ "HealthCheckConfig": self.health_check_config,
+ "HealthCheckCustomConfig": self.health_check_custom_config,
+ "Type": self.service_type,
+ }
+
+
+class Operation(BaseModel):
+ def __init__(self, operation_type, targets):
+ super().__init__()
+ self.id = f"{random_id(32)}-{random_id(8)}"
+ self.status = "SUCCESS"
+ self.operation_type = operation_type
+ self.created = unix_time()
+ self.updated = unix_time()
+ self.targets = targets
+
+ def to_json(self, short=False):
+ if short:
+ return {"Id": self.id, "Status": self.status}
+ else:
+ return {
+ "Id": self.id,
+ "Status": self.status,
+ "Type": self.operation_type,
+ "CreateDate": self.created,
+ "UpdateDate": self.updated,
+ "Targets": self.targets,
+ }
+
+
+class ServiceDiscoveryBackend(BaseBackend):
+ """Implementation of ServiceDiscovery APIs."""
+
+ def __init__(self, region_name=None):
+ self.region_name = region_name
+ self.operations = dict()
+ self.namespaces = dict()
+ self.services = 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 list_namespaces(self):
+ """
+ Pagination or the Filters-parameter is not yet implemented
+ """
+ return self.namespaces.values()
+
+ def create_http_namespace(self, name, creator_request_id, description, tags):
+ namespace = Namespace(
+ region=self.region_name,
+ name=name,
+ ns_type="HTTP",
+ creator_request_id=creator_request_id,
+ description=description,
+ dns_properties={"SOA": {}},
+ http_properties={"HttpName": name},
+ )
+ self.namespaces[namespace.id] = namespace
+ if tags:
+ self.tagger.tag_resource(namespace.arn, tags)
+ operation_id = self._create_operation(
+ "CREATE_NAMESPACE", targets={"NAMESPACE": namespace.id}
+ )
+ return operation_id
+
+ def _create_operation(self, op_type, targets):
+ operation = Operation(operation_type=op_type, targets=targets)
+ self.operations[operation.id] = operation
+ operation_id = operation.id
+ return operation_id
+
+ def delete_namespace(self, namespace_id):
+ if namespace_id not in self.namespaces:
+ raise NamespaceNotFound(namespace_id)
+ del self.namespaces[namespace_id]
+ operation_id = self._create_operation(
+ op_type="DELETE_NAMESPACE", targets={"NAMESPACE": namespace_id}
+ )
+ return operation_id
+
+ def get_namespace(self, namespace_id):
+ if namespace_id not in self.namespaces:
+ raise NamespaceNotFound(namespace_id)
+ return self.namespaces[namespace_id]
+
+ def list_operations(self):
+ """
+ Pagination or the Filters-argument is not yet implemented
+ """
+ # Operations for namespaces will only be listed as long as namespaces exist
+ self.operations = {
+ op_id: op
+ for op_id, op in self.operations.items()
+ if op.targets.get("NAMESPACE") in self.namespaces
+ }
+ return self.operations.values()
+
+ def get_operation(self, operation_id):
+ if operation_id not in self.operations:
+ raise OperationNotFound()
+ return self.operations[operation_id]
+
+ def tag_resource(self, resource_arn, tags):
+ self.tagger.tag_resource(resource_arn, tags)
+
+ def untag_resource(self, resource_arn, tag_keys):
+ self.tagger.untag_resource_using_names(resource_arn, tag_keys)
+
+ def list_tags_for_resource(self, resource_arn):
+ return self.tagger.list_tags_for_resource(resource_arn)
+
+ def create_private_dns_namespace(
+ self, name, creator_request_id, description, vpc, tags, properties
+ ):
+ for namespace in self.namespaces.values():
+ if namespace.vpc == vpc:
+ raise ConflictingDomainExists(vpc)
+ dns_properties = (properties or {}).get("DnsProperties", {})
+ dns_properties["HostedZoneId"] = "hzi"
+ namespace = Namespace(
+ region=self.region_name,
+ name=name,
+ ns_type="DNS_PRIVATE",
+ creator_request_id=creator_request_id,
+ description=description,
+ dns_properties=dns_properties,
+ http_properties={},
+ vpc=vpc,
+ )
+ self.namespaces[namespace.id] = namespace
+ if tags:
+ self.tagger.tag_resource(namespace.arn, tags)
+ operation_id = self._create_operation(
+ "CREATE_NAMESPACE", targets={"NAMESPACE": namespace.id}
+ )
+ return operation_id
+
+ def create_public_dns_namespace(
+ self, name, creator_request_id, description, tags, properties
+ ):
+ dns_properties = (properties or {}).get("DnsProperties", {})
+ dns_properties["HostedZoneId"] = "hzi"
+ namespace = Namespace(
+ region=self.region_name,
+ name=name,
+ ns_type="DNS_PUBLIC",
+ creator_request_id=creator_request_id,
+ description=description,
+ dns_properties=dns_properties,
+ http_properties={},
+ )
+ self.namespaces[namespace.id] = namespace
+ if tags:
+ self.tagger.tag_resource(namespace.arn, tags)
+ operation_id = self._create_operation(
+ "CREATE_NAMESPACE", targets={"NAMESPACE": namespace.id}
+ )
+ return operation_id
+
+ def create_service(
+ self,
+ name,
+ namespace_id,
+ creator_request_id,
+ description,
+ dns_config,
+ health_check_config,
+ health_check_custom_config,
+ tags,
+ service_type,
+ ):
+ service = Service(
+ region=self.region_name,
+ name=name,
+ namespace_id=namespace_id,
+ description=description,
+ creator_request_id=creator_request_id,
+ dns_config=dns_config,
+ health_check_config=health_check_config,
+ health_check_custom_config=health_check_custom_config,
+ service_type=service_type,
+ )
+ self.services[service.id] = service
+ if tags:
+ self.tagger.tag_resource(service.arn, tags)
+ return service
+
+ def get_service(self, service_id):
+ if service_id not in self.services:
+ raise ServiceNotFound(service_id)
+ return self.services[service_id]
+
+ def delete_service(self, service_id):
+ self.services.pop(service_id, None)
+
+ def list_services(self):
+ """
+ Pagination or the Filters-argument is not yet implemented
+ """
+ return self.services.values()
+
+ def update_service(self, service_id, details):
+ service = self.get_service(service_id)
+ service.update(details=details)
+ operation_id = self._create_operation(
+ "UPDATE_SERVICE", targets={"SEVICE": service.id}
+ )
+ return operation_id
+
+
+servicediscovery_backends = BackendDict(ServiceDiscoveryBackend, "servicediscovery")
diff --git a/moto/servicediscovery/responses.py b/moto/servicediscovery/responses.py
new file mode 100644
index 000000000..38873294c
--- /dev/null
+++ b/moto/servicediscovery/responses.py
@@ -0,0 +1,171 @@
+"""Handles incoming servicediscovery requests, invokes methods, returns responses."""
+import json
+
+from moto.core.responses import BaseResponse
+from .models import servicediscovery_backends
+
+
+class ServiceDiscoveryResponse(BaseResponse):
+ @property
+ def servicediscovery_backend(self):
+ """Return backend instance specific for this region."""
+ return servicediscovery_backends[self.region]
+
+ def list_namespaces(self):
+ namespaces = self.servicediscovery_backend.list_namespaces()
+ return 200, {}, json.dumps({"Namespaces": [ns.to_json() for ns in namespaces]})
+
+ def create_http_namespace(self):
+ params = json.loads(self.body)
+ name = params.get("Name")
+ creator_request_id = params.get("CreatorRequestId")
+ description = params.get("Description")
+ tags = params.get("Tags")
+ operation_id = self.servicediscovery_backend.create_http_namespace(
+ name=name,
+ creator_request_id=creator_request_id,
+ description=description,
+ tags=tags,
+ )
+ return json.dumps(dict(OperationId=operation_id))
+
+ def delete_namespace(self):
+ params = json.loads(self.body)
+ namespace_id = params.get("Id")
+ operation_id = self.servicediscovery_backend.delete_namespace(
+ namespace_id=namespace_id,
+ )
+ return json.dumps(dict(OperationId=operation_id))
+
+ def list_operations(self):
+ operations = self.servicediscovery_backend.list_operations()
+ return (
+ 200,
+ {},
+ json.dumps({"Operations": [o.to_json(short=True) for o in operations]}),
+ )
+
+ def get_operation(self):
+ params = json.loads(self.body)
+ operation_id = params.get("OperationId")
+ operation = self.servicediscovery_backend.get_operation(
+ operation_id=operation_id,
+ )
+ return json.dumps(dict(Operation=operation.to_json()))
+
+ def get_namespace(self):
+ params = json.loads(self.body)
+ namespace_id = params.get("Id")
+ namespace = self.servicediscovery_backend.get_namespace(
+ namespace_id=namespace_id,
+ )
+ return json.dumps(dict(Namespace=namespace.to_json()))
+
+ def tag_resource(self):
+ params = json.loads(self.body)
+ resource_arn = params.get("ResourceARN")
+ tags = params.get("Tags")
+ self.servicediscovery_backend.tag_resource(
+ resource_arn=resource_arn, tags=tags,
+ )
+ return json.dumps(dict())
+
+ def untag_resource(self):
+ params = json.loads(self.body)
+ resource_arn = params.get("ResourceARN")
+ tag_keys = params.get("TagKeys")
+ self.servicediscovery_backend.untag_resource(
+ resource_arn=resource_arn, tag_keys=tag_keys,
+ )
+ return json.dumps(dict())
+
+ def list_tags_for_resource(self):
+ params = json.loads(self.body)
+ resource_arn = params.get("ResourceARN")
+ tags = self.servicediscovery_backend.list_tags_for_resource(
+ resource_arn=resource_arn,
+ )
+ return 200, {}, json.dumps(tags)
+
+ def create_private_dns_namespace(self):
+ params = json.loads(self.body)
+ name = params.get("Name")
+ creator_request_id = params.get("CreatorRequestId")
+ description = params.get("Description")
+ vpc = params.get("Vpc")
+ tags = params.get("Tags")
+ properties = params.get("Properties")
+ operation_id = self.servicediscovery_backend.create_private_dns_namespace(
+ name=name,
+ creator_request_id=creator_request_id,
+ description=description,
+ vpc=vpc,
+ tags=tags,
+ properties=properties,
+ )
+ return json.dumps(dict(OperationId=operation_id))
+
+ def create_public_dns_namespace(self):
+ params = json.loads(self.body)
+ name = params.get("Name")
+ creator_request_id = params.get("CreatorRequestId")
+ description = params.get("Description")
+ tags = params.get("Tags")
+ properties = params.get("Properties")
+ operation_id = self.servicediscovery_backend.create_public_dns_namespace(
+ name=name,
+ creator_request_id=creator_request_id,
+ description=description,
+ tags=tags,
+ properties=properties,
+ )
+ return json.dumps(dict(OperationId=operation_id))
+
+ def create_service(self):
+ params = json.loads(self.body)
+ name = params.get("Name")
+ namespace_id = params.get("NamespaceId")
+ creator_request_id = params.get("CreatorRequestId")
+ description = params.get("Description")
+ dns_config = params.get("DnsConfig")
+ health_check_config = params.get("HealthCheckConfig")
+ health_check_custom_config = params.get("HealthCheckCustomConfig")
+ tags = params.get("Tags")
+ service_type = params.get("Type")
+ service = self.servicediscovery_backend.create_service(
+ name=name,
+ namespace_id=namespace_id,
+ creator_request_id=creator_request_id,
+ description=description,
+ dns_config=dns_config,
+ health_check_config=health_check_config,
+ health_check_custom_config=health_check_custom_config,
+ tags=tags,
+ service_type=service_type,
+ )
+ return json.dumps(dict(Service=service.to_json()))
+
+ def get_service(self):
+ params = json.loads(self.body)
+ service_id = params.get("Id")
+ service = self.servicediscovery_backend.get_service(service_id=service_id)
+ return json.dumps(dict(Service=service.to_json()))
+
+ def delete_service(self):
+ params = json.loads(self.body)
+ service_id = params.get("Id")
+ self.servicediscovery_backend.delete_service(service_id=service_id)
+ return json.dumps(dict())
+
+ def list_services(self):
+ services = self.servicediscovery_backend.list_services()
+ return json.dumps(dict(Services=[s.to_json() for s in services]))
+
+ def update_service(self):
+ params = json.loads(self.body)
+ service_id = params.get("Id")
+ details = params.get("Service")
+ operation_id = self.servicediscovery_backend.update_service(
+ service_id=service_id, details=details,
+ )
+ return json.dumps(dict(OperationId=operation_id))
diff --git a/moto/servicediscovery/urls.py b/moto/servicediscovery/urls.py
new file mode 100644
index 000000000..53e1b0ffb
--- /dev/null
+++ b/moto/servicediscovery/urls.py
@@ -0,0 +1,11 @@
+"""servicediscovery base URL and path."""
+from .responses import ServiceDiscoveryResponse
+
+url_bases = [
+ r"https?://servicediscovery\.(.+)\.amazonaws\.com",
+]
+
+
+url_paths = {
+ "{0}/$": ServiceDiscoveryResponse.dispatch,
+}
diff --git a/tests/terraform-tests.success.txt b/tests/terraform-tests.success.txt
index 9e98761e0..a5865a903 100644
--- a/tests/terraform-tests.success.txt
+++ b/tests/terraform-tests.success.txt
@@ -85,6 +85,7 @@ TestAccAWSRedshiftServiceAccount
TestAccAWSRolePolicyAttachment
TestAccAWSSNSSMSPreferences
TestAccAWSSageMakerPrebuiltECRImage
+TestAccAWSServiceDiscovery
TestAccAWSSQSQueuePolicy
TestAccAWSSSMDocument
TestValidateSSMDocumentPermissions
diff --git a/tests/test_servicediscovery/__init__.py b/tests/test_servicediscovery/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/tests/test_servicediscovery/test_server.py b/tests/test_servicediscovery/test_server.py
new file mode 100644
index 000000000..8967f11ba
--- /dev/null
+++ b/tests/test_servicediscovery/test_server.py
@@ -0,0 +1,15 @@
+import json
+import sure # noqa # pylint: disable=unused-import
+
+import moto.server as server
+
+
+def test_servicediscovery_list():
+ backend = server.create_backend_app("servicediscovery")
+ test_client = backend.test_client()
+
+ headers = {"X-Amz-Target": "Route53AutoNaming_v20170314.ListNamespaces"}
+
+ resp = test_client.get("/", headers=headers)
+ resp.status_code.should.equal(200)
+ json.loads(resp.data).should.equal({"Namespaces": []})
diff --git a/tests/test_servicediscovery/test_servicediscovery_httpnamespaces.py b/tests/test_servicediscovery/test_servicediscovery_httpnamespaces.py
new file mode 100644
index 000000000..97998c2ea
--- /dev/null
+++ b/tests/test_servicediscovery/test_servicediscovery_httpnamespaces.py
@@ -0,0 +1,233 @@
+"""Unit tests for servicediscovery-supported APIs."""
+import boto3
+import pytest
+import sure # noqa # pylint: disable=unused-import
+
+from botocore.exceptions import ClientError
+from moto import mock_servicediscovery
+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_servicediscovery
+def test_create_http_namespace():
+ client = boto3.client("servicediscovery", region_name="eu-west-1")
+ client.create_http_namespace(Name="mynamespace")
+
+ resp = client.list_namespaces()
+ resp.should.have.key("Namespaces").length_of(1)
+
+ namespace = resp["Namespaces"][0]
+ namespace.should.have.key("Id").match("ns-[a-z0-9]{16}")
+ namespace.should.have.key("Arn").match(
+ f"arn:aws:servicediscovery:eu-west-1:{ACCOUNT_ID}:namespace/{namespace['Id']}"
+ )
+ namespace.should.have.key("Name").equals("mynamespace")
+ namespace.should.have.key("Type").equals("HTTP")
+ namespace.should.have.key("CreateDate")
+
+ namespace.should.have.key("Properties")
+ props = namespace["Properties"]
+ props.should.have.key("DnsProperties").equals({"SOA": {}})
+ props.should.have.key("HttpProperties").equals({"HttpName": "mynamespace"})
+
+
+@mock_servicediscovery
+def test_get_http_namespace_minimal():
+ client = boto3.client("servicediscovery", region_name="eu-west-1")
+ client.create_http_namespace(Name="mynamespace")
+
+ ns_id = client.list_namespaces()["Namespaces"][0]["Id"]
+
+ resp = client.get_namespace(Id=ns_id)
+ resp.should.have.key("Namespace")
+
+ namespace = resp["Namespace"]
+ namespace.should.have.key("Id").match(ns_id)
+ namespace.should.have.key("Arn").match(
+ f"arn:aws:servicediscovery:eu-west-1:{ACCOUNT_ID}:namespace/{namespace['Id']}"
+ )
+ namespace.should.have.key("Name").equals("mynamespace")
+ namespace.should.have.key("Type").equals("HTTP")
+ namespace.should.have.key("CreateDate")
+ namespace.should.have.key("CreatorRequestId")
+
+ namespace.should.have.key("Properties")
+ props = namespace["Properties"]
+ props.should.have.key("DnsProperties").equals({"SOA": {}})
+ props.should.have.key("HttpProperties").equals({"HttpName": "mynamespace"})
+
+ namespace.shouldnt.have.key("Description")
+
+
+@mock_servicediscovery
+def test_get_http_namespace():
+ client = boto3.client("servicediscovery", region_name="eu-west-1")
+ client.create_http_namespace(
+ Name="mynamespace", CreatorRequestId="crid", Description="mu fancy namespace"
+ )
+
+ ns_id = client.list_namespaces()["Namespaces"][0]["Id"]
+
+ resp = client.get_namespace(Id=ns_id)
+ resp.should.have.key("Namespace")
+
+ namespace = resp["Namespace"]
+ namespace.should.have.key("Id").match(ns_id)
+ namespace.should.have.key("Arn").match(
+ f"arn:aws:servicediscovery:eu-west-1:{ACCOUNT_ID}:namespace/{namespace['Id']}"
+ )
+ namespace.should.have.key("Name").equals("mynamespace")
+ namespace.should.have.key("Type").equals("HTTP")
+ namespace.should.have.key("CreateDate")
+ namespace.should.have.key("CreatorRequestId").equals("crid")
+ namespace.should.have.key("Description").equals("mu fancy namespace")
+
+ namespace.should.have.key("Properties")
+ props = namespace["Properties"]
+ props.should.have.key("DnsProperties").equals({"SOA": {}})
+ props.should.have.key("HttpProperties").equals({"HttpName": "mynamespace"})
+
+
+@mock_servicediscovery
+def test_delete_namespace():
+ client = boto3.client("servicediscovery", region_name="eu-west-1")
+ client.create_http_namespace(Name="mynamespace")
+ ns_id = client.list_namespaces()["Namespaces"][0]["Id"]
+
+ resp = client.delete_namespace(Id=ns_id)
+ resp.should.have.key("OperationId")
+
+ # Calling delete again while this is in progress results in an error:
+ # Another operation of type DeleteNamespace and id dlmpkcn33aovnztwdpsdplgtheuhgcap-k6x64euq is in progress
+ # list_operations is empty after successfull deletion - old operations from this namespace should be deleted
+ # list_namespaces is also empty (obvs)
+
+ client.list_namespaces()["Namespaces"].should.equal([])
+ client.list_operations()["Operations"].should.equal([])
+
+
+@mock_servicediscovery
+def test_delete_unknown_namespace():
+ client = boto3.client("servicediscovery", region_name="eu-west-1")
+ with pytest.raises(ClientError) as exc:
+ client.delete_namespace(Id="unknown")
+ err = exc.value.response["Error"]
+ err["Code"].should.equal("NamespaceNotFound")
+ err["Message"].should.equal("unknown")
+
+
+@mock_servicediscovery
+def test_get_unknown_namespace():
+ client = boto3.client("servicediscovery", region_name="eu-west-1")
+ with pytest.raises(ClientError) as exc:
+ client.get_namespace(Id="unknown")
+ err = exc.value.response["Error"]
+ err["Code"].should.equal("NamespaceNotFound")
+ err["Message"].should.equal("unknown")
+
+
+@mock_servicediscovery
+def test_create_private_dns_namespace_minimal():
+ client = boto3.client("servicediscovery", region_name="eu-west-1")
+ client.create_private_dns_namespace(Name="dns_ns", Vpc="vpc_id")
+
+ ns_id = client.list_namespaces()["Namespaces"][0]["Id"]
+
+ resp = client.get_namespace(Id=ns_id)
+ resp.should.have.key("Namespace")
+
+ namespace = resp["Namespace"]
+ namespace.should.have.key("Id").match(ns_id)
+ namespace.should.have.key("Name").equals("dns_ns")
+ namespace.should.have.key("Type").equals("DNS_PRIVATE")
+
+ namespace.should.have.key("Properties")
+ props = namespace["Properties"]
+ props.should.have.key("DnsProperties")
+ props["DnsProperties"].should.have.key("HostedZoneId")
+ props["DnsProperties"].shouldnt.have.key("SOA")
+
+
+@mock_servicediscovery
+def test_create_private_dns_namespace():
+ client = boto3.client("servicediscovery", region_name="eu-west-1")
+ client.create_private_dns_namespace(
+ Name="dns_ns",
+ Vpc="vpc_id",
+ Description="my private dns",
+ Properties={"DnsProperties": {"SOA": {"TTL": 123}}},
+ )
+
+ ns_id = client.list_namespaces()["Namespaces"][0]["Id"]
+
+ resp = client.get_namespace(Id=ns_id)
+ resp.should.have.key("Namespace")
+
+ namespace = resp["Namespace"]
+ namespace.should.have.key("Id").match(ns_id)
+ namespace.should.have.key("Name").equals("dns_ns")
+ namespace.should.have.key("Type").equals("DNS_PRIVATE")
+ namespace.should.have.key("Description").equals("my private dns")
+
+ namespace.should.have.key("Properties")
+ props = namespace["Properties"]
+ props.should.have.key("DnsProperties")
+ props["DnsProperties"].should.have.key("HostedZoneId")
+ props["DnsProperties"].should.have.key("SOA").equals({"TTL": 123})
+
+
+@mock_servicediscovery
+def test_create_private_dns_namespace_with_duplicate_vpc():
+ client = boto3.client("servicediscovery", region_name="eu-west-1")
+ client.create_private_dns_namespace(Name="dns_ns", Vpc="vpc_id")
+
+ with pytest.raises(ClientError) as exc:
+ client.create_private_dns_namespace(Name="sth else", Vpc="vpc_id")
+ err = exc.value.response["Error"]
+ err["Code"].should.equal("ConflictingDomainExists")
+
+
+@mock_servicediscovery
+def test_create_public_dns_namespace_minimal():
+ client = boto3.client("servicediscovery", region_name="us-east-2")
+ client.create_public_dns_namespace(Name="public_dns_ns")
+
+ ns_id = client.list_namespaces()["Namespaces"][0]["Id"]
+
+ resp = client.get_namespace(Id=ns_id)
+ resp.should.have.key("Namespace")
+
+ namespace = resp["Namespace"]
+ namespace.should.have.key("Id").match(ns_id)
+ namespace.should.have.key("Name").equals("public_dns_ns")
+ namespace.should.have.key("Type").equals("DNS_PUBLIC")
+
+
+@mock_servicediscovery
+def test_create_public_dns_namespace():
+ client = boto3.client("servicediscovery", region_name="us-east-2")
+ client.create_public_dns_namespace(
+ Name="public_dns_ns",
+ CreatorRequestId="cri",
+ Description="my public dns",
+ Properties={"DnsProperties": {"SOA": {"TTL": 124}}},
+ )
+
+ ns_id = client.list_namespaces()["Namespaces"][0]["Id"]
+
+ resp = client.get_namespace(Id=ns_id)
+ resp.should.have.key("Namespace")
+
+ namespace = resp["Namespace"]
+ namespace.should.have.key("Id").match(ns_id)
+ namespace.should.have.key("Name").equals("public_dns_ns")
+ namespace.should.have.key("Type").equals("DNS_PUBLIC")
+ namespace.should.have.key("Description").equals("my public dns")
+ namespace.should.have.key("CreatorRequestId").equals("cri")
+
+ namespace.should.have.key("Properties").should.have.key("DnsProperties")
+ dns_props = namespace["Properties"]["DnsProperties"]
+ dns_props.should.equal({"HostedZoneId": "hzi", "SOA": {"TTL": 124}})
diff --git a/tests/test_servicediscovery/test_servicediscovery_operations.py b/tests/test_servicediscovery/test_servicediscovery_operations.py
new file mode 100644
index 000000000..f42dce14b
--- /dev/null
+++ b/tests/test_servicediscovery/test_servicediscovery_operations.py
@@ -0,0 +1,135 @@
+"""Unit tests for servicediscovery-supported APIs."""
+import boto3
+import pytest
+import sure # noqa # pylint: disable=unused-import
+
+from botocore.exceptions import ClientError
+from moto import mock_servicediscovery
+
+# 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_servicediscovery
+def test_list_operations_initial():
+ client = boto3.client("servicediscovery", region_name="eu-west-1")
+ resp = client.list_operations()
+
+ resp.should.have.key("Operations").equals([])
+
+
+@mock_servicediscovery
+def test_list_operations():
+ client = boto3.client("servicediscovery", region_name="eu-west-2")
+
+ resp = client.create_http_namespace(Name="n/a")
+ resp.should.have.key("OperationId")
+ op_id = resp["OperationId"]
+
+ resp = client.list_operations()
+ resp.should.have.key("Operations").length_of(1)
+ resp["Operations"].should.equal([{"Id": op_id, "Status": "SUCCESS"}])
+
+
+@mock_servicediscovery
+def test_get_create_http_namespace_operation():
+ client = boto3.client("servicediscovery", region_name="eu-west-1")
+ resp = client.create_http_namespace(Name="mynamespace")
+
+ resp["OperationId"].should.match("[a-z0-9]{32}-[a-z0-9]{8}")
+
+ operation_id = resp["OperationId"]
+
+ resp = client.get_operation(OperationId=operation_id)
+
+ resp.should.have.key("Operation")
+ operation = resp["Operation"]
+ operation.should.have.key("Id").equals(operation_id)
+ operation.should.have.key("Type").equals("CREATE_NAMESPACE")
+ operation.should.have.key("Status").equals("SUCCESS")
+ operation.should.have.key("CreateDate")
+ operation.should.have.key("UpdateDate")
+ operation.should.have.key("Targets")
+
+ targets = operation["Targets"]
+ targets.should.have.key("NAMESPACE")
+
+ namespaces = client.list_namespaces()["Namespaces"]
+ [ns["Id"] for ns in namespaces].should.contain(targets["NAMESPACE"])
+
+
+@mock_servicediscovery
+def test_get_private_dns_namespace_operation():
+ client = boto3.client("servicediscovery", region_name="eu-west-1")
+ resp = client.create_private_dns_namespace(Name="dns_ns", Vpc="vpc_id")
+
+ resp["OperationId"].should.match("[a-z0-9]{32}-[a-z0-9]{8}")
+
+ operation_id = resp["OperationId"]
+
+ resp = client.get_operation(OperationId=operation_id)
+
+ resp.should.have.key("Operation")
+ operation = resp["Operation"]
+ operation.should.have.key("Id").equals(operation_id)
+ operation.should.have.key("Type").equals("CREATE_NAMESPACE")
+ operation.should.have.key("Status").equals("SUCCESS")
+ operation.should.have.key("CreateDate")
+ operation.should.have.key("UpdateDate")
+ operation.should.have.key("Targets")
+
+
+@mock_servicediscovery
+def test_get_public_dns_namespace_operation():
+ client = boto3.client("servicediscovery", region_name="eu-west-1")
+ resp = client.create_public_dns_namespace(Name="dns_ns")
+
+ resp["OperationId"].should.match("[a-z0-9]{32}-[a-z0-9]{8}")
+
+ operation_id = resp["OperationId"]
+
+ resp = client.get_operation(OperationId=operation_id)
+
+ resp.should.have.key("Operation")
+ operation = resp["Operation"]
+ operation.should.have.key("Id").equals(operation_id)
+ operation.should.have.key("Type").equals("CREATE_NAMESPACE")
+ operation.should.have.key("Status").equals("SUCCESS")
+ operation.should.have.key("CreateDate")
+ operation.should.have.key("UpdateDate")
+ operation.should.have.key("Targets")
+
+
+@mock_servicediscovery
+def test_get_update_service_operation():
+ client = boto3.client("servicediscovery", region_name="eu-west-1")
+ service_id = client.create_service(
+ Name="my service", NamespaceId="ns_id", Description="first desc",
+ )["Service"]["Id"]
+
+ resp = client.update_service(Id=service_id, Service={"Description": "updated desc"})
+
+ resp["OperationId"].should.match("[a-z0-9]{32}-[a-z0-9]{8}")
+
+ operation_id = resp["OperationId"]
+
+ resp = client.get_operation(OperationId=operation_id)
+
+ resp.should.have.key("Operation")
+ operation = resp["Operation"]
+ operation.should.have.key("Id").equals(operation_id)
+ operation.should.have.key("Type").equals("UPDATE_SERVICE")
+ operation.should.have.key("Status").equals("SUCCESS")
+ operation.should.have.key("CreateDate")
+ operation.should.have.key("UpdateDate")
+ operation.should.have.key("Targets")
+
+
+@mock_servicediscovery
+def test_get_unknown_operation():
+ client = boto3.client("servicediscovery", region_name="eu-west-1")
+
+ with pytest.raises(ClientError) as exc:
+ client.get_operation(OperationId="unknown")
+ err = exc.value.response["Error"]
+ err["Code"].should.equal("OperationNotFound")
diff --git a/tests/test_servicediscovery/test_servicediscovery_service.py b/tests/test_servicediscovery/test_servicediscovery_service.py
new file mode 100644
index 000000000..66f1c943b
--- /dev/null
+++ b/tests/test_servicediscovery/test_servicediscovery_service.py
@@ -0,0 +1,208 @@
+"""Unit tests for servicediscovery-supported APIs."""
+import boto3
+import pytest
+import sure # noqa # pylint: disable=unused-import
+
+from botocore.exceptions import ClientError
+from moto import mock_servicediscovery
+
+# 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_servicediscovery
+def test_create_service_minimal():
+ client = boto3.client("servicediscovery", region_name="ap-southeast-1")
+ operation_id = client.create_http_namespace(Name="mynamespace")["OperationId"]
+ namespace_id = client.get_operation(OperationId=operation_id)["Operation"][
+ "Targets"
+ ]["NAMESPACE"]
+
+ resp = client.create_service(Name="my service", NamespaceId=namespace_id)
+
+ resp.should.have.key("Service")
+ resp["Service"].should.have.key("Id")
+ resp["Service"].should.have.key("Arn")
+ resp["Service"].should.have.key("Name").equals("my service")
+ resp["Service"].should.have.key("NamespaceId").equals(namespace_id)
+ resp["Service"].should.have.key("CreateDate")
+
+
+@mock_servicediscovery
+def test_create_service():
+ client = boto3.client("servicediscovery", region_name="ap-southeast-1")
+ operation_id = client.create_http_namespace(Name="mynamespace")["OperationId"]
+ namespace_id = client.get_operation(OperationId=operation_id)["Operation"][
+ "Targets"
+ ]["NAMESPACE"]
+
+ resp = client.create_service(
+ Name="my service",
+ CreatorRequestId="crid",
+ Description="my service",
+ DnsConfig={
+ "NamespaceId": namespace_id,
+ "RoutingPolicy": "WEIGHTED",
+ "DnsRecords": [{"Type": "SRV", "TTL": 0}],
+ },
+ HealthCheckConfig={"Type": "TCP", "ResourcePath": "/sth"},
+ HealthCheckCustomConfig={"FailureThreshold": 125},
+ Type="HTTP",
+ )
+
+ resp.should.have.key("Service")
+ resp["Service"].should.have.key("Id")
+ resp["Service"].should.have.key("Arn")
+ resp["Service"].should.have.key("Name").equals("my service")
+ resp["Service"].shouldnt.have.key("NamespaceId")
+ resp["Service"].should.have.key("Description").equals("my service")
+ resp["Service"].should.have.key("DnsConfig").equals(
+ {
+ "NamespaceId": namespace_id,
+ "RoutingPolicy": "WEIGHTED",
+ "DnsRecords": [{"Type": "SRV", "TTL": 0}],
+ }
+ )
+ resp["Service"].should.have.key("HealthCheckConfig").equals(
+ {"Type": "TCP", "ResourcePath": "/sth"}
+ )
+ resp["Service"].should.have.key("HealthCheckCustomConfig").equals(
+ {"FailureThreshold": 125}
+ )
+ resp["Service"].should.have.key("Type").equals("HTTP")
+ resp["Service"].should.have.key("CreatorRequestId").equals("crid")
+
+
+@mock_servicediscovery
+def test_get_service():
+ client = boto3.client("servicediscovery", region_name="ap-southeast-1")
+
+ operation_id = client.create_http_namespace(Name="mynamespace")["OperationId"]
+ namespace_id = client.get_operation(OperationId=operation_id)["Operation"][
+ "Targets"
+ ]["NAMESPACE"]
+
+ service_id = client.create_service(Name="my service", NamespaceId=namespace_id)[
+ "Service"
+ ]["Id"]
+
+ resp = client.get_service(Id=service_id)
+
+ resp.should.have.key("Service")
+ resp["Service"].should.have.key("Id")
+ resp["Service"].should.have.key("Arn")
+ resp["Service"].should.have.key("Name").equals("my service")
+ resp["Service"].should.have.key("NamespaceId").equals(namespace_id)
+
+
+@mock_servicediscovery
+def test_get_unknown_service():
+ client = boto3.client("servicediscovery", region_name="ap-southeast-1")
+
+ with pytest.raises(ClientError) as exc:
+ client.get_service(Id="unknown")
+ err = exc.value.response["Error"]
+ err["Code"].should.equal("ServiceNotFound")
+ err["Message"].should.equal("unknown")
+
+
+@mock_servicediscovery
+def test_delete_service():
+ client = boto3.client("servicediscovery", region_name="eu-west-1")
+
+ operation_id = client.create_http_namespace(Name="mynamespace")["OperationId"]
+ namespace_id = client.get_operation(OperationId=operation_id)["Operation"][
+ "Targets"
+ ]["NAMESPACE"]
+ service_id = client.create_service(Name="my service", NamespaceId=namespace_id)[
+ "Service"
+ ]["Id"]
+
+ client.delete_service(Id=service_id)
+
+ with pytest.raises(ClientError) as exc:
+ client.get_service(Id=service_id)
+ err = exc.value.response["Error"]
+ err["Code"].should.equal("ServiceNotFound")
+ err["Message"].should.equal(service_id)
+
+
+@mock_servicediscovery
+def test_update_service_description():
+ client = boto3.client("servicediscovery", region_name="ap-southeast-1")
+ operation_id = client.create_http_namespace(Name="mynamespace")["OperationId"]
+ namespace_id = client.get_operation(OperationId=operation_id)["Operation"][
+ "Targets"
+ ]["NAMESPACE"]
+
+ service_id = client.create_service(
+ Name="my service",
+ NamespaceId=namespace_id,
+ Description="first desc",
+ DnsConfig={
+ "NamespaceId": namespace_id,
+ "RoutingPolicy": "WEIGHTED",
+ "DnsRecords": [{"Type": "SRV", "TTL": 0}],
+ },
+ HealthCheckConfig={"Type": "TCP", "ResourcePath": "/sth"},
+ )["Service"]["Id"]
+
+ client.update_service(Id=service_id, Service={"Description": "updated desc"})
+
+ resp = client.get_service(Id=service_id)
+
+ resp.should.have.key("Service")
+ resp["Service"].should.have.key("Id").equals(service_id)
+ resp["Service"].should.have.key("Arn")
+ resp["Service"].should.have.key("Name").equals("my service")
+ resp["Service"].should.have.key("NamespaceId").equals(namespace_id)
+ resp["Service"].should.have.key("Description").equals("updated desc")
+ # From the docs:
+ # If you omit any existing DnsRecords or HealthCheckConfig configurations from an UpdateService request,
+ # the configurations are deleted from the service.
+ resp["Service"].shouldnt.have.key("DnsConfig")
+ resp["Service"].should.have.key("HealthCheckConfig").equals(
+ {"Type": "TCP", "ResourcePath": "/sth"}
+ )
+
+
+@mock_servicediscovery
+def test_update_service_others():
+ client = boto3.client("servicediscovery", region_name="ap-southeast-1")
+ operation_id = client.create_http_namespace(Name="mynamespace")["OperationId"]
+ namespace_id = client.get_operation(OperationId=operation_id)["Operation"][
+ "Targets"
+ ]["NAMESPACE"]
+
+ service_id = client.create_service(
+ Name="my service",
+ NamespaceId=namespace_id,
+ Description="first desc",
+ DnsConfig={
+ "RoutingPolicy": "WEIGHTED",
+ "DnsRecords": [{"Type": "SRV", "TTL": 0}],
+ },
+ )["Service"]["Id"]
+
+ client.update_service(
+ Id=service_id,
+ Service={
+ "DnsConfig": {"DnsRecords": [{"Type": "SRV", "TTL": 12}]},
+ "HealthCheckConfig": {"Type": "TCP", "ResourcePath": "/sth"},
+ },
+ )
+
+ resp = client.get_service(Id=service_id)
+
+ resp.should.have.key("Service")
+ resp["Service"].should.have.key("Id").equals(service_id)
+ resp["Service"].should.have.key("Arn")
+ resp["Service"].should.have.key("Name").equals("my service")
+ resp["Service"].should.have.key("NamespaceId").equals(namespace_id)
+ resp["Service"].should.have.key("Description").equals("first desc")
+ resp["Service"].should.have.key("DnsConfig").equals(
+ {"RoutingPolicy": "WEIGHTED", "DnsRecords": [{"Type": "SRV", "TTL": 12}]}
+ )
+ resp["Service"].should.have.key("HealthCheckConfig").equals(
+ {"Type": "TCP", "ResourcePath": "/sth"}
+ )
diff --git a/tests/test_servicediscovery/test_servicediscovery_tags.py b/tests/test_servicediscovery/test_servicediscovery_tags.py
new file mode 100644
index 000000000..1f11e26fd
--- /dev/null
+++ b/tests/test_servicediscovery/test_servicediscovery_tags.py
@@ -0,0 +1,103 @@
+"""Unit tests for servicediscovery-supported APIs."""
+import boto3
+
+import sure # noqa # pylint: disable=unused-import
+from moto import mock_servicediscovery
+
+# 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_servicediscovery
+def test_create_http_namespace_with_tags():
+ client = boto3.client("servicediscovery", region_name="eu-west-1")
+ client.create_http_namespace(
+ Name="mynamespace", Tags=[{"Key": "key1", "Value": "val1"}]
+ )
+
+ ns_arn = client.list_namespaces()["Namespaces"][0]["Arn"]
+
+ resp = client.list_tags_for_resource(ResourceARN=ns_arn)
+ resp.should.have.key("Tags")
+
+ resp["Tags"].should.equal([{"Key": "key1", "Value": "val1"}])
+
+
+@mock_servicediscovery
+def test_create_public_dns_namespace_with_tags():
+ client = boto3.client("servicediscovery", region_name="eu-west-1")
+ client.create_public_dns_namespace(
+ Name="mynamespace", Tags=[{"Key": "key1", "Value": "val1"}]
+ )
+
+ ns_arn = client.list_namespaces()["Namespaces"][0]["Arn"]
+
+ resp = client.list_tags_for_resource(ResourceARN=ns_arn)
+ resp.should.have.key("Tags")
+
+ resp["Tags"].should.equal([{"Key": "key1", "Value": "val1"}])
+
+
+@mock_servicediscovery
+def test_create_private_dns_namespace_with_tags():
+ client = boto3.client("servicediscovery", region_name="eu-west-1")
+ client.create_private_dns_namespace(
+ Name="mynamespace", Vpc="vpc", Tags=[{"Key": "key1", "Value": "val1"}]
+ )
+
+ ns_arn = client.list_namespaces()["Namespaces"][0]["Arn"]
+
+ resp = client.list_tags_for_resource(ResourceARN=ns_arn)
+ resp.should.have.key("Tags")
+
+ resp["Tags"].should.equal([{"Key": "key1", "Value": "val1"}])
+
+
+@mock_servicediscovery
+def test_create_service_with_tags():
+ client = boto3.client("servicediscovery", region_name="eu-west-1")
+ client.create_service(Name="myservice", Tags=[{"Key": "key1", "Value": "val1"}])
+
+ ns_arn = client.list_services()["Services"][0]["Arn"]
+
+ resp = client.list_tags_for_resource(ResourceARN=ns_arn)
+ resp.should.have.key("Tags")
+
+ resp["Tags"].should.equal([{"Key": "key1", "Value": "val1"}])
+
+
+@mock_servicediscovery
+def test_tag_resource():
+ client = boto3.client("servicediscovery", region_name="ap-southeast-1")
+ client.create_http_namespace(
+ Name="mynamespace", Tags=[{"Key": "key1", "Value": "val1"}]
+ )
+
+ ns_arn = client.list_namespaces()["Namespaces"][0]["Arn"]
+ client.tag_resource(ResourceARN=ns_arn, Tags=[{"Key": "key2", "Value": "val2"}])
+
+ resp = client.list_tags_for_resource(ResourceARN=ns_arn)
+ resp.should.have.key("Tags")
+
+ resp["Tags"].should.equal(
+ [{"Key": "key1", "Value": "val1"}, {"Key": "key2", "Value": "val2"}]
+ )
+
+
+@mock_servicediscovery
+def test_untag_resource():
+ client = boto3.client("servicediscovery", region_name="us-east-2")
+ client.create_http_namespace(Name="mynamespace")
+
+ ns_arn = client.list_namespaces()["Namespaces"][0]["Arn"]
+ client.tag_resource(
+ ResourceARN=ns_arn,
+ Tags=[{"Key": "key1", "Value": "val1"}, {"Key": "key2", "Value": "val2"}],
+ )
+
+ client.untag_resource(ResourceARN=ns_arn, TagKeys=["key1"])
+
+ resp = client.list_tags_for_resource(ResourceARN=ns_arn)
+ resp.should.have.key("Tags")
+
+ resp["Tags"].should.equal([{"Key": "key2", "Value": "val2"}])