Feature: ElasticsearchService (#4703)
This commit is contained in:
parent
cd0f0ee83c
commit
82d18443d3
@ -2191,6 +2191,52 @@
|
||||
- [ ] untag_resource
|
||||
</details>
|
||||
|
||||
## es
|
||||
<details>
|
||||
<summary>10% implemented</summary>
|
||||
|
||||
- [ ] accept_inbound_cross_cluster_search_connection
|
||||
- [ ] add_tags
|
||||
- [ ] associate_package
|
||||
- [ ] cancel_elasticsearch_service_software_update
|
||||
- [X] create_elasticsearch_domain
|
||||
- [ ] create_outbound_cross_cluster_search_connection
|
||||
- [ ] create_package
|
||||
- [X] delete_elasticsearch_domain
|
||||
- [ ] delete_elasticsearch_service_role
|
||||
- [ ] delete_inbound_cross_cluster_search_connection
|
||||
- [ ] delete_outbound_cross_cluster_search_connection
|
||||
- [ ] delete_package
|
||||
- [ ] describe_domain_auto_tunes
|
||||
- [X] describe_elasticsearch_domain
|
||||
- [ ] describe_elasticsearch_domain_config
|
||||
- [ ] describe_elasticsearch_domains
|
||||
- [ ] describe_elasticsearch_instance_type_limits
|
||||
- [ ] describe_inbound_cross_cluster_search_connections
|
||||
- [ ] describe_outbound_cross_cluster_search_connections
|
||||
- [ ] describe_packages
|
||||
- [ ] describe_reserved_elasticsearch_instance_offerings
|
||||
- [ ] describe_reserved_elasticsearch_instances
|
||||
- [ ] dissociate_package
|
||||
- [ ] get_compatible_elasticsearch_versions
|
||||
- [ ] get_package_version_history
|
||||
- [ ] get_upgrade_history
|
||||
- [ ] get_upgrade_status
|
||||
- [X] list_domain_names
|
||||
- [ ] list_domains_for_package
|
||||
- [ ] list_elasticsearch_instance_types
|
||||
- [ ] list_elasticsearch_versions
|
||||
- [ ] list_packages_for_domain
|
||||
- [ ] list_tags
|
||||
- [ ] purchase_reserved_elasticsearch_instance_offering
|
||||
- [ ] reject_inbound_cross_cluster_search_connection
|
||||
- [ ] remove_tags
|
||||
- [ ] start_elasticsearch_service_software_update
|
||||
- [ ] update_elasticsearch_domain_config
|
||||
- [ ] update_package
|
||||
- [ ] upgrade_elasticsearch_domain
|
||||
</details>
|
||||
|
||||
## events
|
||||
<details>
|
||||
<summary>78% implemented</summary>
|
||||
@ -5054,7 +5100,6 @@
|
||||
- ebs
|
||||
- ecr-public
|
||||
- elastic-inference
|
||||
- es
|
||||
- evidently
|
||||
- finspace
|
||||
- finspace-data
|
||||
|
75
docs/docs/services/es.rst
Normal file
75
docs/docs/services/es.rst
Normal file
@ -0,0 +1,75 @@
|
||||
.. _implementedservice_es:
|
||||
|
||||
.. |start-h3| raw:: html
|
||||
|
||||
<h3>
|
||||
|
||||
.. |end-h3| raw:: html
|
||||
|
||||
</h3>
|
||||
|
||||
==
|
||||
es
|
||||
==
|
||||
|
||||
.. autoclass:: moto.es.models.ElasticsearchServiceBackend
|
||||
|
||||
|start-h3| Example usage |end-h3|
|
||||
|
||||
.. sourcecode:: python
|
||||
|
||||
@mock_es
|
||||
def test_es_behaviour:
|
||||
boto3.client("es")
|
||||
...
|
||||
|
||||
|
||||
|
||||
|start-h3| Implemented features for this service |end-h3|
|
||||
|
||||
- [ ] accept_inbound_cross_cluster_search_connection
|
||||
- [ ] add_tags
|
||||
- [ ] associate_package
|
||||
- [ ] cancel_elasticsearch_service_software_update
|
||||
- [X] create_elasticsearch_domain
|
||||
- [ ] create_outbound_cross_cluster_search_connection
|
||||
- [ ] create_package
|
||||
- [X] delete_elasticsearch_domain
|
||||
- [ ] delete_elasticsearch_service_role
|
||||
- [ ] delete_inbound_cross_cluster_search_connection
|
||||
- [ ] delete_outbound_cross_cluster_search_connection
|
||||
- [ ] delete_package
|
||||
- [ ] describe_domain_auto_tunes
|
||||
- [X] describe_elasticsearch_domain
|
||||
- [ ] describe_elasticsearch_domain_config
|
||||
- [ ] describe_elasticsearch_domains
|
||||
- [ ] describe_elasticsearch_instance_type_limits
|
||||
- [ ] describe_inbound_cross_cluster_search_connections
|
||||
- [ ] describe_outbound_cross_cluster_search_connections
|
||||
- [ ] describe_packages
|
||||
- [ ] describe_reserved_elasticsearch_instance_offerings
|
||||
- [ ] describe_reserved_elasticsearch_instances
|
||||
- [ ] dissociate_package
|
||||
- [ ] get_compatible_elasticsearch_versions
|
||||
- [ ] get_package_version_history
|
||||
- [ ] get_upgrade_history
|
||||
- [ ] get_upgrade_status
|
||||
- [X] list_domain_names
|
||||
|
||||
The engine-type parameter is not yet supported.
|
||||
Pagination is not yet implemented.
|
||||
|
||||
|
||||
- [ ] list_domains_for_package
|
||||
- [ ] list_elasticsearch_instance_types
|
||||
- [ ] list_elasticsearch_versions
|
||||
- [ ] list_packages_for_domain
|
||||
- [ ] list_tags
|
||||
- [ ] purchase_reserved_elasticsearch_instance_offering
|
||||
- [ ] reject_inbound_cross_cluster_search_connection
|
||||
- [ ] remove_tags
|
||||
- [ ] start_elasticsearch_service_software_update
|
||||
- [ ] update_elasticsearch_domain_config
|
||||
- [ ] update_package
|
||||
- [ ] upgrade_elasticsearch_domain
|
||||
|
@ -89,6 +89,7 @@ mock_emr_deprecated = lazy_load(".emr", "mock_emr_deprecated")
|
||||
mock_emrcontainers = lazy_load(
|
||||
".emrcontainers", "mock_emrcontainers", boto3_name="emr-containers"
|
||||
)
|
||||
mock_es = lazy_load(".es", "mock_es")
|
||||
mock_events = lazy_load(".events", "mock_events")
|
||||
mock_firehose = lazy_load(".firehose", "mock_firehose")
|
||||
mock_forecast = lazy_load(".forecast", "mock_forecast")
|
||||
|
@ -61,6 +61,7 @@ backend_url_patterns = [
|
||||
("emr", re.compile("https?://(.+)\\.elasticmapreduce\\.amazonaws.com")),
|
||||
("emr", re.compile("https?://elasticmapreduce\\.(.+)\\.amazonaws.com")),
|
||||
("emr-containers", re.compile("https?://emr-containers\\.(.+)\\.amazonaws\\.com")),
|
||||
("es", re.compile("https?://es\\.(.+)\\.amazonaws\\.com")),
|
||||
("events", re.compile("https?://events\\.(.+)\\.amazonaws\\.com")),
|
||||
("firehose", re.compile("https?://firehose\\.(.+)\\.amazonaws\\.com")),
|
||||
("forecast", re.compile("https?://forecast\\.(.+)\\.amazonaws\\.com")),
|
||||
|
5
moto/es/__init__.py
Normal file
5
moto/es/__init__.py
Normal file
@ -0,0 +1,5 @@
|
||||
"""es module initialization; sets value for base decorator."""
|
||||
from .models import es_backends
|
||||
from ..core.models import base_decorator
|
||||
|
||||
mock_es = base_decorator(es_backends)
|
25
moto/es/exceptions.py
Normal file
25
moto/es/exceptions.py
Normal file
@ -0,0 +1,25 @@
|
||||
"""Exceptions raised by the ElasticSearch service."""
|
||||
from moto.core.exceptions import JsonRESTError
|
||||
|
||||
|
||||
class ElasticSearchError(JsonRESTError):
|
||||
code = 400
|
||||
|
||||
|
||||
class ResourceNotFound(ElasticSearchError):
|
||||
code = 409
|
||||
|
||||
def __init__(self, resource_type, resource_name):
|
||||
msg = f"{resource_type} not found: {resource_name}"
|
||||
super().__init__("ResourceNotFoundException", msg)
|
||||
|
||||
|
||||
class InvalidDomainName(ElasticSearchError):
|
||||
def __init__(self, domain_name):
|
||||
msg = f"1 validation error detected: Value '{domain_name}' at 'domainName' failed to satisfy constraint: Member must satisfy regular expression pattern: [a-z][a-z0-9\\-]+"
|
||||
super().__init__("ValidationException", msg)
|
||||
|
||||
|
||||
class DomainNotFound(ResourceNotFound):
|
||||
def __init__(self, domain_name):
|
||||
super().__init__("Domain", domain_name)
|
160
moto/es/models.py
Normal file
160
moto/es/models.py
Normal file
@ -0,0 +1,160 @@
|
||||
from boto3 import Session
|
||||
|
||||
from moto.core import BaseBackend, BaseModel
|
||||
from moto.core.utils import get_random_hex
|
||||
from .exceptions import DomainNotFound
|
||||
|
||||
|
||||
class Domain(BaseModel):
|
||||
def __init__(
|
||||
self,
|
||||
region_name,
|
||||
domain_name,
|
||||
es_version,
|
||||
elasticsearch_cluster_config,
|
||||
ebs_options,
|
||||
access_policies,
|
||||
snapshot_options,
|
||||
vpc_options,
|
||||
cognito_options,
|
||||
encryption_at_rest_options,
|
||||
node_to_node_encryption_options,
|
||||
advanced_options,
|
||||
log_publishing_options,
|
||||
domain_endpoint_options,
|
||||
advanced_security_options,
|
||||
auto_tune_options,
|
||||
tag_list,
|
||||
):
|
||||
self.domain_id = get_random_hex(8)
|
||||
self.region_name = region_name
|
||||
self.domain_name = domain_name
|
||||
self.es_version = es_version
|
||||
self.elasticsearch_cluster_config = elasticsearch_cluster_config
|
||||
self.ebs_options = ebs_options
|
||||
self.access_policies = access_policies
|
||||
self.snapshot_options = snapshot_options
|
||||
self.vpc_options = vpc_options
|
||||
self.cognito_options = cognito_options
|
||||
self.encryption_at_rest_options = encryption_at_rest_options
|
||||
self.node_to_node_encryption_options = node_to_node_encryption_options
|
||||
self.advanced_options = advanced_options
|
||||
self.log_publishing_options = log_publishing_options
|
||||
self.domain_endpoint_options = domain_endpoint_options
|
||||
self.advanced_security_options = advanced_security_options
|
||||
self.auto_tune_options = auto_tune_options
|
||||
if self.auto_tune_options:
|
||||
self.auto_tune_options["State"] = "ENABLED"
|
||||
|
||||
@property
|
||||
def arn(self):
|
||||
return f"arn:aws:es:{self.region_name}:domain/{self.domain_id}"
|
||||
|
||||
def to_json(self):
|
||||
return {
|
||||
"DomainId": self.domain_id,
|
||||
"DomainName": self.domain_name,
|
||||
"ARN": self.arn,
|
||||
"Created": True,
|
||||
"Deleted": False,
|
||||
"Processing": False,
|
||||
"UpgradeProcessing": False,
|
||||
"ElasticsearchVersion": self.es_version,
|
||||
"ElasticsearchClusterConfig": self.elasticsearch_cluster_config,
|
||||
"EBSOptions": self.ebs_options,
|
||||
"AccessPolicies": self.access_policies,
|
||||
"SnapshotOptions": self.snapshot_options,
|
||||
"VPCOptions": self.vpc_options,
|
||||
"CognitoOptions": self.cognito_options,
|
||||
"EncryptionAtRestOptions": self.encryption_at_rest_options,
|
||||
"NodeToNodeEncryptionOptions": self.node_to_node_encryption_options,
|
||||
"AdvancedOptions": self.advanced_options,
|
||||
"LogPublishingOptions": self.log_publishing_options,
|
||||
"DomainEndpointOptions": self.domain_endpoint_options,
|
||||
"AdvancedSecurityOptions": self.advanced_security_options,
|
||||
"AutoTuneOptions": self.auto_tune_options,
|
||||
}
|
||||
|
||||
|
||||
class ElasticsearchServiceBackend(BaseBackend):
|
||||
"""Implementation of ElasticsearchService APIs."""
|
||||
|
||||
def __init__(self, region_name=None):
|
||||
self.region_name = region_name
|
||||
self.domains = dict()
|
||||
|
||||
def reset(self):
|
||||
"""Re-initialize all attributes for this instance."""
|
||||
region_name = self.region_name
|
||||
self.__dict__ = {}
|
||||
self.__init__(region_name)
|
||||
|
||||
def create_elasticsearch_domain(
|
||||
self,
|
||||
domain_name,
|
||||
elasticsearch_version,
|
||||
elasticsearch_cluster_config,
|
||||
ebs_options,
|
||||
access_policies,
|
||||
snapshot_options,
|
||||
vpc_options,
|
||||
cognito_options,
|
||||
encryption_at_rest_options,
|
||||
node_to_node_encryption_options,
|
||||
advanced_options,
|
||||
log_publishing_options,
|
||||
domain_endpoint_options,
|
||||
advanced_security_options,
|
||||
auto_tune_options,
|
||||
tag_list,
|
||||
):
|
||||
# TODO: Persist/Return other attributes
|
||||
new_domain = Domain(
|
||||
region_name=self.region_name,
|
||||
domain_name=domain_name,
|
||||
es_version=elasticsearch_version,
|
||||
elasticsearch_cluster_config=elasticsearch_cluster_config,
|
||||
ebs_options=ebs_options,
|
||||
access_policies=access_policies,
|
||||
snapshot_options=snapshot_options,
|
||||
vpc_options=vpc_options,
|
||||
cognito_options=cognito_options,
|
||||
encryption_at_rest_options=encryption_at_rest_options,
|
||||
node_to_node_encryption_options=node_to_node_encryption_options,
|
||||
advanced_options=advanced_options,
|
||||
log_publishing_options=log_publishing_options,
|
||||
domain_endpoint_options=domain_endpoint_options,
|
||||
advanced_security_options=advanced_security_options,
|
||||
auto_tune_options=auto_tune_options,
|
||||
tag_list=tag_list,
|
||||
)
|
||||
self.domains[domain_name] = new_domain
|
||||
return new_domain.to_json()
|
||||
|
||||
def delete_elasticsearch_domain(self, domain_name):
|
||||
if domain_name not in self.domains:
|
||||
raise DomainNotFound(domain_name)
|
||||
del self.domains[domain_name]
|
||||
|
||||
def describe_elasticsearch_domain(self, domain_name):
|
||||
if domain_name not in self.domains:
|
||||
raise DomainNotFound(domain_name)
|
||||
return self.domains[domain_name].to_json()
|
||||
|
||||
def list_domain_names(self, engine_type):
|
||||
"""
|
||||
The engine-type parameter is not yet supported.
|
||||
Pagination is not yet implemented.
|
||||
"""
|
||||
return [{"DomainName": domain.domain_name} for domain in self.domains.values()]
|
||||
|
||||
|
||||
es_backends = {}
|
||||
for available_region in Session().get_available_regions("es"):
|
||||
es_backends[available_region] = ElasticsearchServiceBackend(available_region)
|
||||
for available_region in Session().get_available_regions(
|
||||
"es", partition_name="aws-us-gov"
|
||||
):
|
||||
es_backends[available_region] = ElasticsearchServiceBackend(available_region)
|
||||
for available_region in Session().get_available_regions("es", partition_name="aws-cn"):
|
||||
es_backends[available_region] = ElasticsearchServiceBackend(available_region)
|
113
moto/es/responses.py
Normal file
113
moto/es/responses.py
Normal file
@ -0,0 +1,113 @@
|
||||
import json
|
||||
import re
|
||||
|
||||
from functools import wraps
|
||||
from moto.core.responses import BaseResponse
|
||||
from .exceptions import ElasticSearchError, InvalidDomainName
|
||||
from .models import es_backends
|
||||
|
||||
|
||||
def error_handler(f):
|
||||
@wraps(f)
|
||||
def _wrapper(*args, **kwargs):
|
||||
try:
|
||||
return f(*args, **kwargs)
|
||||
except ElasticSearchError as e:
|
||||
return e.code, e.get_headers(), e.get_body()
|
||||
|
||||
return _wrapper
|
||||
|
||||
|
||||
class ElasticsearchServiceResponse(BaseResponse):
|
||||
"""Handler for ElasticsearchService requests and responses."""
|
||||
|
||||
@property
|
||||
def es_backend(self):
|
||||
"""Return backend instance specific for this region."""
|
||||
return es_backends[self.region]
|
||||
|
||||
@classmethod
|
||||
@error_handler
|
||||
def list_domains(cls, request, full_url, headers):
|
||||
response = ElasticsearchServiceResponse()
|
||||
response.setup_class(request, full_url, headers)
|
||||
if request.method == "GET":
|
||||
return response.list_domain_names()
|
||||
|
||||
@classmethod
|
||||
@error_handler
|
||||
def domains(cls, request, full_url, headers):
|
||||
response = ElasticsearchServiceResponse()
|
||||
response.setup_class(request, full_url, headers)
|
||||
if request.method == "POST":
|
||||
return response.create_elasticsearch_domain()
|
||||
|
||||
@classmethod
|
||||
@error_handler
|
||||
def domain(cls, request, full_url, headers):
|
||||
response = ElasticsearchServiceResponse()
|
||||
response.setup_class(request, full_url, headers)
|
||||
if request.method == "DELETE":
|
||||
return response.delete_elasticsearch_domain()
|
||||
if request.method == "GET":
|
||||
return response.describe_elasticsearch_domain()
|
||||
|
||||
def create_elasticsearch_domain(self):
|
||||
params = json.loads(self.body)
|
||||
domain_name = params.get("DomainName")
|
||||
if not re.match(r"^[a-z][a-z0-9\-]+$", domain_name):
|
||||
raise InvalidDomainName(domain_name)
|
||||
elasticsearch_version = params.get("ElasticsearchVersion")
|
||||
elasticsearch_cluster_config = params.get("ElasticsearchClusterConfig")
|
||||
ebs_options = params.get("EBSOptions")
|
||||
access_policies = params.get("AccessPolicies")
|
||||
snapshot_options = params.get("SnapshotOptions")
|
||||
vpc_options = params.get("VPCOptions")
|
||||
cognito_options = params.get("CognitoOptions")
|
||||
encryption_at_rest_options = params.get("EncryptionAtRestOptions")
|
||||
node_to_node_encryption_options = params.get("NodeToNodeEncryptionOptions")
|
||||
advanced_options = params.get("AdvancedOptions")
|
||||
log_publishing_options = params.get("LogPublishingOptions")
|
||||
domain_endpoint_options = params.get("DomainEndpointOptions")
|
||||
advanced_security_options = params.get("AdvancedSecurityOptions")
|
||||
auto_tune_options = params.get("AutoTuneOptions")
|
||||
tag_list = params.get("TagList")
|
||||
domain_status = self.es_backend.create_elasticsearch_domain(
|
||||
domain_name=domain_name,
|
||||
elasticsearch_version=elasticsearch_version,
|
||||
elasticsearch_cluster_config=elasticsearch_cluster_config,
|
||||
ebs_options=ebs_options,
|
||||
access_policies=access_policies,
|
||||
snapshot_options=snapshot_options,
|
||||
vpc_options=vpc_options,
|
||||
cognito_options=cognito_options,
|
||||
encryption_at_rest_options=encryption_at_rest_options,
|
||||
node_to_node_encryption_options=node_to_node_encryption_options,
|
||||
advanced_options=advanced_options,
|
||||
log_publishing_options=log_publishing_options,
|
||||
domain_endpoint_options=domain_endpoint_options,
|
||||
advanced_security_options=advanced_security_options,
|
||||
auto_tune_options=auto_tune_options,
|
||||
tag_list=tag_list,
|
||||
)
|
||||
return 200, {}, json.dumps({"DomainStatus": domain_status})
|
||||
|
||||
def delete_elasticsearch_domain(self):
|
||||
domain_name = self.path.split("/")[-1]
|
||||
self.es_backend.delete_elasticsearch_domain(domain_name=domain_name,)
|
||||
return 200, {}, json.dumps(dict())
|
||||
|
||||
def describe_elasticsearch_domain(self):
|
||||
domain_name = self.path.split("/")[-1]
|
||||
if not re.match(r"^[a-z][a-z0-9\-]+$", domain_name):
|
||||
raise InvalidDomainName(domain_name)
|
||||
domain_status = self.es_backend.describe_elasticsearch_domain(
|
||||
domain_name=domain_name,
|
||||
)
|
||||
return 200, {}, json.dumps({"DomainStatus": domain_status})
|
||||
|
||||
def list_domain_names(self):
|
||||
params = self._get_params()
|
||||
engine_type = params.get("EngineType")
|
||||
domain_names = self.es_backend.list_domain_names(engine_type=engine_type,)
|
||||
return 200, {}, json.dumps({"DomainNames": domain_names})
|
12
moto/es/urls.py
Normal file
12
moto/es/urls.py
Normal file
@ -0,0 +1,12 @@
|
||||
from .responses import ElasticsearchServiceResponse
|
||||
|
||||
url_bases = [
|
||||
r"https?://es\.(.+)\.amazonaws\.com",
|
||||
]
|
||||
|
||||
|
||||
url_paths = {
|
||||
"{0}/2015-01-01/domain$": ElasticsearchServiceResponse.list_domains,
|
||||
"{0}/2015-01-01/es/domain$": ElasticsearchServiceResponse.domains,
|
||||
"{0}/2015-01-01/es/domain/(?P<domainname>[^/]+)": ElasticsearchServiceResponse.domain,
|
||||
}
|
0
tests/test_es/__init__.py
Normal file
0
tests/test_es/__init__.py
Normal file
236
tests/test_es/test_es.py
Normal file
236
tests/test_es/test_es.py
Normal file
@ -0,0 +1,236 @@
|
||||
"""Unit tests for es-supported APIs."""
|
||||
import boto3
|
||||
import pytest
|
||||
import sure # noqa # pylint: disable=unused-import
|
||||
from botocore.exceptions import ClientError
|
||||
from moto import mock_es
|
||||
|
||||
# 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
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"name", ["getmoto.org", "search-is-$$$", "dev_or_test", "dev/test", "1love", "DEV"]
|
||||
)
|
||||
@mock_es
|
||||
def test_create_domain_invalid_name(name):
|
||||
client = boto3.client("es", region_name="us-east-2")
|
||||
with pytest.raises(ClientError) as exc:
|
||||
client.create_elasticsearch_domain(DomainName=name)
|
||||
err = exc.value.response["Error"]
|
||||
err["Message"].should.equal(
|
||||
f"1 validation error detected: Value '{name}' at 'domainName' failed to satisfy constraint: Member must satisfy regular expression pattern: [a-z][a-z0-9\\-]+"
|
||||
)
|
||||
err["Code"].should.equal("ValidationException")
|
||||
|
||||
|
||||
@mock_es
|
||||
def test_create_elasticsearch_domain_minimal():
|
||||
client = boto3.client("es", region_name="us-east-2")
|
||||
resp = client.create_elasticsearch_domain(DomainName="motosearch")
|
||||
|
||||
resp.should.have.key("DomainStatus")
|
||||
domain = resp["DomainStatus"]
|
||||
domain.should.have.key("DomainName").equals("motosearch")
|
||||
domain.should.have.key("DomainId")
|
||||
domain.should.have.key("ARN").equals(
|
||||
f"arn:aws:es:us-east-2:domain/{domain['DomainId']}"
|
||||
)
|
||||
domain.should.have.key("Created").equals(True)
|
||||
domain.should.have.key("Deleted").equals(False)
|
||||
domain.should.have.key("Processing").equals(False)
|
||||
domain.should.have.key("UpgradeProcessing").equals(False)
|
||||
domain.shouldnt.have.key("ElasticsearchVersion")
|
||||
|
||||
|
||||
@mock_es
|
||||
def test_create_elasticsearch_domain():
|
||||
client = boto3.client("es", region_name="us-east-2")
|
||||
resp = client.create_elasticsearch_domain(
|
||||
DomainName="motosearch",
|
||||
ElasticsearchVersion="7.10",
|
||||
ElasticsearchClusterConfig={
|
||||
"InstanceType": "m3.large.elasticsearch",
|
||||
"InstanceCount": 1,
|
||||
"DedicatedMasterEnabled": True,
|
||||
"DedicatedMasterType": "m3.large.elasticsearch",
|
||||
"DedicatedMasterCount": 1,
|
||||
"ZoneAwarenessEnabled": False,
|
||||
"WarmEnabled": False,
|
||||
"ColdStorageOptions": {"Enabled": False},
|
||||
},
|
||||
EBSOptions={
|
||||
"EBSEnabled": True,
|
||||
"VolumeType": "io2",
|
||||
"VolumeSize": 10,
|
||||
"Iops": 1,
|
||||
},
|
||||
AccessPolicies="some unvalidated accesspolicy",
|
||||
SnapshotOptions={"AutomatedSnapshotStartHour": 1,},
|
||||
VPCOptions={"SubnetIds": ["s1"], "SecurityGroupIds": ["sg1"]},
|
||||
CognitoOptions={"Enabled": False},
|
||||
EncryptionAtRestOptions={"Enabled": False},
|
||||
NodeToNodeEncryptionOptions={"Enabled": False},
|
||||
AdvancedOptions={"option": "value"},
|
||||
LogPublishingOptions={"log1": {"Enabled": False}},
|
||||
DomainEndpointOptions={"EnforceHTTPS": True, "CustomEndpointEnabled": False,},
|
||||
AdvancedSecurityOptions={"Enabled": False},
|
||||
AutoTuneOptions={"DesiredState": "ENABLED"},
|
||||
)
|
||||
|
||||
domain = resp["DomainStatus"]
|
||||
domain.should.have.key("DomainId")
|
||||
domain.should.have.key("Created").equals(True)
|
||||
domain.should.have.key("ElasticsearchVersion").equals("7.10")
|
||||
|
||||
domain.should.have.key("ElasticsearchClusterConfig")
|
||||
cluster_config = domain["ElasticsearchClusterConfig"]
|
||||
cluster_config.should.have.key("ColdStorageOptions").equals({"Enabled": False})
|
||||
cluster_config.should.have.key("DedicatedMasterCount").equals(1)
|
||||
cluster_config.should.have.key("DedicatedMasterType").equals(
|
||||
"m3.large.elasticsearch"
|
||||
)
|
||||
cluster_config.should.have.key("WarmEnabled").equals(False)
|
||||
|
||||
domain.should.have.key("EBSOptions")
|
||||
ebs = domain["EBSOptions"]
|
||||
ebs.should.have.key("EBSEnabled").equals(True)
|
||||
ebs.should.have.key("Iops").equals(1)
|
||||
ebs.should.have.key("VolumeSize").equals(10)
|
||||
ebs.should.have.key("VolumeType").equals("io2")
|
||||
|
||||
domain.should.have.key("AccessPolicies").equals("some unvalidated accesspolicy")
|
||||
|
||||
domain.should.have.key("SnapshotOptions")
|
||||
snapshots = domain["SnapshotOptions"]
|
||||
snapshots.should.have.key("AutomatedSnapshotStartHour").equals(1)
|
||||
|
||||
domain.should.have.key("VPCOptions")
|
||||
vpcs = domain["VPCOptions"]
|
||||
vpcs.should.have.key("SubnetIds").equals(["s1"])
|
||||
vpcs.should.have.key("SecurityGroupIds").equals(["sg1"])
|
||||
|
||||
domain.should.have.key("CognitoOptions")
|
||||
cognito = domain["CognitoOptions"]
|
||||
cognito.should.have.key("Enabled").equals(False)
|
||||
|
||||
domain.should.have.key("EncryptionAtRestOptions")
|
||||
encryption_at_rest = domain["EncryptionAtRestOptions"]
|
||||
encryption_at_rest.should.have.key("Enabled").equals(False)
|
||||
|
||||
domain.should.have.key("NodeToNodeEncryptionOptions")
|
||||
encryption = domain["NodeToNodeEncryptionOptions"]
|
||||
encryption.should.have.key("Enabled").equals(False)
|
||||
|
||||
domain.should.have.key("AdvancedOptions")
|
||||
advanced = domain["AdvancedOptions"]
|
||||
advanced.should.have.key("option").equals("value")
|
||||
|
||||
domain.should.have.key("LogPublishingOptions")
|
||||
advanced = domain["LogPublishingOptions"]
|
||||
advanced.should.have.key("log1").equals({"Enabled": False})
|
||||
|
||||
domain.should.have.key("DomainEndpointOptions")
|
||||
endpoint = domain["DomainEndpointOptions"]
|
||||
endpoint.should.have.key("EnforceHTTPS").equals(True)
|
||||
endpoint.should.have.key("CustomEndpointEnabled").equals(False)
|
||||
|
||||
domain.should.have.key("AdvancedSecurityOptions")
|
||||
advanced_security = domain["AdvancedSecurityOptions"]
|
||||
advanced_security.should.have.key("Enabled").equals(False)
|
||||
|
||||
domain.should.have.key("AutoTuneOptions")
|
||||
auto_tune = domain["AutoTuneOptions"]
|
||||
auto_tune.should.have.key("State").equals("ENABLED")
|
||||
|
||||
|
||||
@mock_es
|
||||
def test_delete_elasticsearch_domain():
|
||||
client = boto3.client("es", region_name="ap-southeast-1")
|
||||
client.create_elasticsearch_domain(DomainName="motosearch")
|
||||
client.delete_elasticsearch_domain(DomainName="motosearch")
|
||||
|
||||
client.list_domain_names()["DomainNames"].should.equal([])
|
||||
|
||||
|
||||
@mock_es
|
||||
def test_missing_delete_elasticsearch_domain():
|
||||
client = boto3.client("es", region_name="ap-southeast-1")
|
||||
with pytest.raises(ClientError) as exc:
|
||||
client.delete_elasticsearch_domain(DomainName="unknown")
|
||||
|
||||
meta = exc.value.response["ResponseMetadata"]
|
||||
meta["HTTPStatusCode"].should.equal(409)
|
||||
|
||||
err = exc.value.response["Error"]
|
||||
err["Code"].should.equal("ResourceNotFoundException")
|
||||
err["Message"].should.equal("Domain not found: unknown")
|
||||
|
||||
|
||||
@mock_es
|
||||
def test_describe_invalid_domain():
|
||||
client = boto3.client("es", region_name="us-east-2")
|
||||
with pytest.raises(ClientError) as exc:
|
||||
client.describe_elasticsearch_domain(DomainName="moto.org")
|
||||
meta = exc.value.response["ResponseMetadata"]
|
||||
meta["HTTPStatusCode"].should.equal(400)
|
||||
err = exc.value.response["Error"]
|
||||
err["Message"].should.equal(
|
||||
f"1 validation error detected: Value 'moto.org' at 'domainName' failed to satisfy constraint: Member must satisfy regular expression pattern: [a-z][a-z0-9\\-]+"
|
||||
)
|
||||
err["Code"].should.equal("ValidationException")
|
||||
|
||||
|
||||
@mock_es
|
||||
def test_describe_unknown_domain():
|
||||
client = boto3.client("es", region_name="ap-southeast-1")
|
||||
with pytest.raises(ClientError) as exc:
|
||||
client.describe_elasticsearch_domain(DomainName="unknown")
|
||||
|
||||
meta = exc.value.response["ResponseMetadata"]
|
||||
meta["HTTPStatusCode"].should.equal(409)
|
||||
|
||||
err = exc.value.response["Error"]
|
||||
err["Code"].should.equal("ResourceNotFoundException")
|
||||
err["Message"].should.equal("Domain not found: unknown")
|
||||
|
||||
|
||||
@mock_es
|
||||
def test_describe_elasticsearch_domain():
|
||||
client = boto3.client("es", region_name="ap-southeast-1")
|
||||
client.create_elasticsearch_domain(DomainName="motosearch")
|
||||
resp = client.describe_elasticsearch_domain(DomainName="motosearch")
|
||||
|
||||
resp.should.have.key("DomainStatus")
|
||||
domain = resp["DomainStatus"]
|
||||
domain.should.have.key("DomainName").equals("motosearch")
|
||||
domain.should.have.key("DomainId")
|
||||
domain.should.have.key("ARN").equals(
|
||||
f"arn:aws:es:ap-southeast-1:domain/{domain['DomainId']}"
|
||||
)
|
||||
domain.should.have.key("Created").equals(True)
|
||||
domain.should.have.key("Deleted").equals(False)
|
||||
domain.should.have.key("Processing").equals(False)
|
||||
domain.should.have.key("UpgradeProcessing").equals(False)
|
||||
domain.shouldnt.have.key("ElasticsearchVersion")
|
||||
|
||||
|
||||
@mock_es
|
||||
def test_list_domain_names_initial():
|
||||
client = boto3.client("es", region_name="eu-west-1")
|
||||
resp = client.list_domain_names()
|
||||
|
||||
resp.should.have.key("DomainNames").equals([])
|
||||
|
||||
|
||||
@mock_es
|
||||
def test_list_domain_names_with_multiple_domains():
|
||||
client = boto3.client("es", region_name="eu-west-1")
|
||||
domain_names = [f"env{i}" for i in range(1, 5)]
|
||||
for name in domain_names:
|
||||
client.create_elasticsearch_domain(DomainName=name)
|
||||
resp = client.list_domain_names()
|
||||
|
||||
resp.should.have.key("DomainNames").length_of(4)
|
||||
for name in domain_names:
|
||||
resp["DomainNames"].should.contain({"DomainName": name})
|
13
tests/test_es/test_server.py
Normal file
13
tests/test_es/test_server.py
Normal file
@ -0,0 +1,13 @@
|
||||
import json
|
||||
import sure # noqa # pylint: disable=unused-import
|
||||
|
||||
import moto.server as server
|
||||
|
||||
|
||||
def test_es_list():
|
||||
backend = server.create_backend_app("es")
|
||||
test_client = backend.test_client()
|
||||
|
||||
resp = test_client.get("/2015-01-01/domain")
|
||||
resp.status_code.should.equal(200)
|
||||
json.loads(resp.data).should.equals({"DomainNames": []})
|
Loading…
Reference in New Issue
Block a user