1""" 2 Tests for salt.modules.boto3_elasticsearch 3""" 4 5 6import datetime 7import random 8import string 9import textwrap 10 11import salt.loader 12import salt.modules.boto3_elasticsearch as boto3_elasticsearch 13from salt.utils.versions import LooseVersion 14from tests.support.mixins import LoaderModuleMockMixin 15from tests.support.mock import MagicMock, patch 16from tests.support.unit import TestCase, skipIf 17 18try: 19 import boto3 20 from botocore.exceptions import ClientError 21 22 HAS_BOTO3 = True 23except ImportError: 24 HAS_BOTO3 = False 25 26# the boto3_elasticsearch module relies on the connect_to_region() method 27# which was added in boto 2.8.0 28# https://github.com/boto/boto/commit/33ac26b416fbb48a60602542b4ce15dcc7029f12 29REQUIRED_BOTO3_VERSION = "1.2.1" 30 31 32def __virtual__(): 33 """ 34 Returns True/False boolean depending on if Boto3 is installed and correct 35 version. 36 """ 37 if not HAS_BOTO3: 38 return False 39 if LooseVersion(boto3.__version__) < LooseVersion(REQUIRED_BOTO3_VERSION): 40 return ( 41 False, 42 "The boto3 module must be greater or equal to version {}".format( 43 REQUIRED_BOTO3_VERSION 44 ), 45 ) 46 return True 47 48 49REGION = "us-east-1" 50ACCESS_KEY = "GKTADJGHEIQSXMKKRBJ08H" 51SECRET_KEY = "askdjghsdfjkghWupUjasdflkdfklgjsdfjajkghs" 52CONN_PARAMETERS = { 53 "region": REGION, 54 "key": ACCESS_KEY, 55 "keyid": SECRET_KEY, 56 "profile": {}, 57} 58ERROR_MESSAGE = ( 59 "An error occurred ({}) when calling the {} operation: Test-defined error" 60) 61ERROR_CONTENT = {"Error": {"Code": 101, "Message": "Test-defined error"}} 62NOT_FOUND_ERROR = ClientError( 63 {"Error": {"Code": "ResourceNotFoundException", "Message": "Test-defined error"}}, 64 "msg", 65) 66DOMAIN_RET = { 67 "DomainId": "accountno/testdomain", 68 "DomainName": "testdomain", 69 "ARN": "arn:aws:es:region:accountno:domain/testdomain", 70 "Created": True, 71 "Deleted": False, 72 "Endpoints": {"vpc": "vpc-testdomain-1234567890.region.es.amazonaws.com"}, 73 "Processing": False, 74 "UpgradeProcessing": False, 75 "ElasticsearchVersion": "6.3", 76 "ElasticsearchClusterConfig": { 77 "InstanceType": "t2.medium.elasticsearch", 78 "InstanceCount": 1, 79 "DedicatedMasterEnabled": False, 80 "ZoneAwarenessEnabled": False, 81 }, 82 "EBSOptions": { 83 "EBSEnabled": True, 84 "VolumeType": "gp2", 85 "VolumeSize": 123, 86 "Iops": 12, 87 }, 88 "AccessPolicies": textwrap.dedent( 89 """ 90 {"Version":"2012-10-17","Statement":[{"Effect":"Allow", 91 "Principal":{"AWS":"*"},"Action":"es:*", 92 "Resource":"arn:aws:es:region:accountno:domain/testdomain/*"}]}""" 93 ), 94 "SnapshotOptions": {"AutomatedSnapshotStartHour": 1}, 95 "VPCOptions": { 96 "VPCId": "vpc-12345678", 97 "SubnetIds": ["subnet-deadbeef"], 98 "AvailabilityZones": ["regiona"], 99 "SecurityGroupIds": ["sg-87654321"], 100 }, 101 "CognitoOptions": {"Enabled": False}, 102 "EncryptionAtRestOptions": {"Enabled": False}, 103 "NodeToNodeEncryptionOptions": {"Enabled": False}, 104 "AdvancedOptions": {"rest.action.multi.allow_explicit_index": "true"}, 105 "ServiceSoftwareOptions": { 106 "CurrentVersion": "R20190221-P1", 107 "NewVersion": "R20190418", 108 "UpdateAvailable": True, 109 "Cancellable": False, 110 "UpdateStatus": "ELIGIBLE", 111 "Description": ( 112 "A newer release R20190418 is available. This release " 113 "will be automatically deployed after somedate" 114 ), 115 "AutomatedUpdateDate": None, 116 }, 117} 118 119 120@skipIf(HAS_BOTO3 is False, "The boto module must be installed.") 121@skipIf( 122 LooseVersion(boto3.__version__) < LooseVersion(REQUIRED_BOTO3_VERSION), 123 "The boto3 module must be greater or equal to version {}".format( 124 REQUIRED_BOTO3_VERSION 125 ), 126) 127class Boto3ElasticsearchTestCase(TestCase, LoaderModuleMockMixin): 128 """ 129 TestCase for salt.modules.boto3_elasticsearch module 130 """ 131 132 conn = None 133 134 def setup_loader_modules(self): 135 self.opts = salt.config.DEFAULT_MINION_OPTS.copy() 136 utils = salt.loader.utils( 137 self.opts, 138 whitelist=["boto3", "args", "systemd", "path", "platform"], 139 context={}, 140 ) 141 return {boto3_elasticsearch: {"__utils__": utils}} 142 143 def setUp(self): 144 super().setUp() 145 boto3_elasticsearch.__init__(self.opts) 146 del self.opts 147 148 # Set up MagicMock to replace the boto3 session 149 # connections keep getting cached from prior tests, can't find the 150 # correct context object to clear it. So randomize the cache key, to prevent any 151 # cache hits 152 CONN_PARAMETERS["key"] = "".join( 153 random.choice(string.ascii_lowercase + string.digits) for _ in range(50) 154 ) 155 156 self.conn = MagicMock() 157 self.addCleanup(delattr, self, "conn") 158 self.patcher = patch("boto3.session.Session") 159 self.addCleanup(self.patcher.stop) 160 self.addCleanup(delattr, self, "patcher") 161 mock_session = self.patcher.start() 162 session_instance = mock_session.return_value 163 session_instance.configure_mock(client=MagicMock(return_value=self.conn)) 164 self.paginator = MagicMock() 165 self.addCleanup(delattr, self, "paginator") 166 self.conn.configure_mock(get_paginator=MagicMock(return_value=self.paginator)) 167 168 def test_describe_elasticsearch_domain_positive(self): 169 """ 170 Test that when describing a domain when the domain actually exists, 171 the .exists method returns a dict with 'result': True 172 and 'response' with the domain status information. 173 """ 174 # The patch below is not neccesary per se, 175 # as .exists returns positive as long as no exception is raised. 176 with patch.object( 177 self.conn, 178 "describe_elasticsearch_domain", 179 return_value={"DomainStatus": DOMAIN_RET}, 180 ): 181 self.assertEqual( 182 boto3_elasticsearch.describe_elasticsearch_domain( 183 domain_name="testdomain", **CONN_PARAMETERS 184 ), 185 {"result": True, "response": DOMAIN_RET}, 186 ) 187 188 def test_describe_elasticsearch_domain_error(self): 189 """ 190 Test that when describing a domain when the domain does not exist, 191 the .exists method returns a dict with 'result': False 192 and 'error' with boto's ResourceNotFoundException. 193 """ 194 with patch.object( 195 self.conn, "describe_elasticsearch_domain", side_effect=NOT_FOUND_ERROR 196 ): 197 result = boto3_elasticsearch.describe_elasticsearch_domain( 198 domain_name="testdomain", **CONN_PARAMETERS 199 ) 200 self.assertEqual( 201 result.get("error", ""), 202 ERROR_MESSAGE.format("ResourceNotFoundException", "msg"), 203 ) 204 self.assertFalse(result["result"]) 205 206 def test_create_elasticsearch_domain_positive(self): 207 """ 208 Test that when creating a domain, and it succeeds, 209 the .create method returns a dict with 'result': True 210 and 'response' with the newly created domain's status information. 211 """ 212 with patch.object( 213 self.conn, 214 "create_elasticsearch_domain", 215 return_value={"DomainStatus": DOMAIN_RET}, 216 ): 217 kwargs = { 218 "elasticsearch_version": DOMAIN_RET["ElasticsearchVersion"], 219 "elasticsearch_cluster_config": DOMAIN_RET[ 220 "ElasticsearchClusterConfig" 221 ], 222 "ebs_options": DOMAIN_RET["EBSOptions"], 223 "access_policies": DOMAIN_RET["AccessPolicies"], 224 "snapshot_options": DOMAIN_RET["SnapshotOptions"], 225 "vpc_options": DOMAIN_RET["VPCOptions"], 226 "cognito_options": DOMAIN_RET["CognitoOptions"], 227 "encryption_at_rest_options": DOMAIN_RET["EncryptionAtRestOptions"], 228 "advanced_options": DOMAIN_RET["AdvancedOptions"], 229 } 230 kwargs.update(CONN_PARAMETERS) 231 self.assertEqual( 232 boto3_elasticsearch.create_elasticsearch_domain( 233 domain_name="testdomain", **kwargs 234 ), 235 {"result": True, "response": DOMAIN_RET}, 236 ) 237 238 def test_create_elasticsearch_domain_error(self): 239 """ 240 Test that when creating a domain, and boto3 returns an error, 241 the .create method returns a dict with 'result': False 242 and 'error' with the error reported by boto3. 243 """ 244 with patch.object( 245 self.conn, 246 "create_elasticsearch_domain", 247 side_effect=ClientError(ERROR_CONTENT, "create_domain"), 248 ): 249 kwargs = { 250 "elasticsearch_version": DOMAIN_RET["ElasticsearchVersion"], 251 "elasticsearch_cluster_config": DOMAIN_RET[ 252 "ElasticsearchClusterConfig" 253 ], 254 "ebs_options": DOMAIN_RET["EBSOptions"], 255 "access_policies": DOMAIN_RET["AccessPolicies"], 256 "snapshot_options": DOMAIN_RET["SnapshotOptions"], 257 "vpc_options": DOMAIN_RET["VPCOptions"], 258 "cognito_options": DOMAIN_RET["CognitoOptions"], 259 "encryption_at_rest_options": DOMAIN_RET["EncryptionAtRestOptions"], 260 "advanced_options": DOMAIN_RET["AdvancedOptions"], 261 } 262 kwargs.update(CONN_PARAMETERS) 263 result = boto3_elasticsearch.create_elasticsearch_domain( 264 "testdomain", **kwargs 265 ) 266 self.assertEqual( 267 result.get("error", ""), ERROR_MESSAGE.format(101, "create_domain") 268 ) 269 270 def test_delete_domain_positive(self): 271 """ 272 Test that when deleting a domain, and it succeeds, 273 the .delete method returns {'result': True}. 274 """ 275 with patch.object(self.conn, "delete_elasticsearch_domain"): 276 self.assertEqual( 277 boto3_elasticsearch.delete_elasticsearch_domain( 278 "testdomain", **CONN_PARAMETERS 279 ), 280 {"result": True}, 281 ) 282 283 def test_delete_domain_error(self): 284 """ 285 Test that when deleting a domain, and boto3 returns an error, 286 the .delete method returns {'result': False, 'error' :'the error'}. 287 """ 288 with patch.object( 289 self.conn, 290 "delete_elasticsearch_domain", 291 side_effect=ClientError(ERROR_CONTENT, "delete_domain"), 292 ): 293 result = boto3_elasticsearch.delete_elasticsearch_domain( 294 "testdomain", **CONN_PARAMETERS 295 ) 296 self.assertFalse(result["result"]) 297 self.assertEqual( 298 result.get("error", ""), ERROR_MESSAGE.format(101, "delete_domain") 299 ) 300 301 def test_update_domain_positive(self): 302 """ 303 Test that when updating a domain succeeds, the .update method returns {'result': True}. 304 """ 305 with patch.object( 306 self.conn, 307 "update_elasticsearch_domain_config", 308 return_value={"DomainConfig": DOMAIN_RET}, 309 ): 310 kwargs = { 311 "elasticsearch_cluster_config": DOMAIN_RET[ 312 "ElasticsearchClusterConfig" 313 ], 314 "ebs_options": DOMAIN_RET["EBSOptions"], 315 "snapshot_options": DOMAIN_RET["SnapshotOptions"], 316 "vpc_options": DOMAIN_RET["VPCOptions"], 317 "cognito_options": DOMAIN_RET["CognitoOptions"], 318 "advanced_options": DOMAIN_RET["AdvancedOptions"], 319 "access_policies": DOMAIN_RET["AccessPolicies"], 320 "log_publishing_options": {}, 321 } 322 323 kwargs.update(CONN_PARAMETERS) 324 self.assertEqual( 325 boto3_elasticsearch.update_elasticsearch_domain_config( 326 "testdomain", **kwargs 327 ), 328 {"result": True, "response": DOMAIN_RET}, 329 ) 330 331 def test_update_domain_error(self): 332 """ 333 Test that when updating a domain fails, and boto3 returns an error, 334 the .update method returns the error. 335 """ 336 with patch.object( 337 self.conn, 338 "update_elasticsearch_domain_config", 339 side_effect=ClientError(ERROR_CONTENT, "update_domain"), 340 ): 341 kwargs = { 342 "elasticsearch_cluster_config": DOMAIN_RET[ 343 "ElasticsearchClusterConfig" 344 ], 345 "ebs_options": DOMAIN_RET["EBSOptions"], 346 "snapshot_options": DOMAIN_RET["SnapshotOptions"], 347 "vpc_options": DOMAIN_RET["VPCOptions"], 348 "cognito_options": DOMAIN_RET["CognitoOptions"], 349 "advanced_options": DOMAIN_RET["AdvancedOptions"], 350 "access_policies": DOMAIN_RET["AccessPolicies"], 351 "log_publishing_options": {}, 352 } 353 kwargs.update(CONN_PARAMETERS) 354 result = boto3_elasticsearch.update_elasticsearch_domain_config( 355 "testdomain", **kwargs 356 ) 357 self.assertEqual( 358 result.get("error", ""), ERROR_MESSAGE.format(101, "update_domain") 359 ) 360 361 def test_add_tags_positive(self): 362 """ 363 Test that when adding tags is successful, the .add_tags method returns {'result': True}. 364 """ 365 with patch.object( 366 self.conn, 367 "describe_elasticsearch_domain", 368 return_value={"DomainStatus": DOMAIN_RET}, 369 ): 370 self.assertEqual( 371 boto3_elasticsearch.add_tags( 372 "testdomain", tags={"foo": "bar", "baz": "qux"}, **CONN_PARAMETERS 373 ), 374 {"result": True}, 375 ) 376 377 def test_add_tags_default(self): 378 """ 379 Test that when tags are not provided, no error is raised. 380 """ 381 with patch.object( 382 self.conn, 383 "describe_elasticsearch_domain", 384 return_value={"DomainStatus": DOMAIN_RET}, 385 ): 386 self.assertEqual( 387 boto3_elasticsearch.add_tags("testdomain", **CONN_PARAMETERS), 388 {"result": True}, 389 ) 390 391 def test_add_tags_error(self): 392 """ 393 Test that when adding tags fails, and boto3 returns an error, 394 the .add_tags function returns {'tagged': False, 'error': 'the error'}. 395 """ 396 with patch.object( 397 self.conn, "add_tags", side_effect=ClientError(ERROR_CONTENT, "add_tags") 398 ), patch.object( 399 self.conn, 400 "describe_elasticsearch_domain", 401 return_value={"DomainStatus": DOMAIN_RET}, 402 ): 403 result = boto3_elasticsearch.add_tags( 404 "testdomain", tags={"foo": "bar", "baz": "qux"}, **CONN_PARAMETERS 405 ) 406 self.assertFalse(result["result"]) 407 self.assertEqual( 408 result.get("error", ""), ERROR_MESSAGE.format(101, "add_tags") 409 ) 410 411 def test_remove_tags_positive(self): 412 """ 413 Test that when removing tags is successful, the .remove_tags method returns {'tagged': True}. 414 """ 415 with patch.object( 416 self.conn, 417 "describe_elasticsearch_domain", 418 return_value={"DomainStatus": DOMAIN_RET}, 419 ): 420 self.assertEqual( 421 boto3_elasticsearch.remove_tags( 422 tag_keys=["foo", "bar"], domain_name="testdomain", **CONN_PARAMETERS 423 ), 424 {"result": True}, 425 ) 426 427 def test_remove_tag_error(self): 428 """ 429 Test that when removing tags fails, and boto3 returns an error, 430 the .remove_tags method returns {'tagged': False, 'error': 'the error'}. 431 """ 432 with patch.object( 433 self.conn, 434 "remove_tags", 435 side_effect=ClientError(ERROR_CONTENT, "remove_tags"), 436 ), patch.object( 437 self.conn, 438 "describe_elasticsearch_domain", 439 return_value={"DomainStatus": DOMAIN_RET}, 440 ): 441 result = boto3_elasticsearch.remove_tags( 442 tag_keys=["foo", "bar"], domain_name="testdomain", **CONN_PARAMETERS 443 ) 444 self.assertFalse(result["result"]) 445 self.assertEqual( 446 result.get("error", ""), ERROR_MESSAGE.format(101, "remove_tags") 447 ) 448 449 def test_list_tags_positive(self): 450 """ 451 Test that when listing tags is successful, 452 the .list_tags method returns a dict with key 'tags'. 453 Also test that the tags returned are manipulated properly (i.e. transformed 454 into a dict with tags). 455 """ 456 with patch.object( 457 self.conn, 458 "describe_elasticsearch_domain", 459 return_value={"DomainStatus": DOMAIN_RET}, 460 ), patch.object( 461 self.conn, 462 "list_tags", 463 return_value={"TagList": [{"Key": "foo", "Value": "bar"}]}, 464 ): 465 result = boto3_elasticsearch.list_tags( 466 domain_name="testdomain", **CONN_PARAMETERS 467 ) 468 self.assertEqual(result, {"result": True, "response": {"foo": "bar"}}) 469 470 def test_list_tags_error(self): 471 """ 472 Test that when listing tags causes boto3 to return an error, 473 the .list_tags method returns the error. 474 """ 475 with patch.object( 476 self.conn, "list_tags", side_effect=ClientError(ERROR_CONTENT, "list_tags") 477 ), patch.object( 478 self.conn, 479 "describe_elasticsearch_domain", 480 return_value={"DomainStatus": DOMAIN_RET}, 481 ): 482 result = boto3_elasticsearch.list_tags( 483 domain_name="testdomain", **CONN_PARAMETERS 484 ) 485 self.assertFalse(result["result"]) 486 self.assertEqual( 487 result.get("error", ""), ERROR_MESSAGE.format(101, "list_tags") 488 ) 489 490 def test_cancel_elasticsearch_service_software_update_positive(self): 491 """ 492 Test that when calling cancel_elasticsearch_service_software_update and 493 it is successful, it returns {'result': True}. 494 """ 495 retval = { 496 "ServiceSoftwareOptions": { 497 "CurrentVersion": "string", 498 "NewVersion": "string", 499 "UpdateAvailable": True, 500 "Cancellable": True, 501 "UpdateStatus": "ELIGIBLE", 502 "Description": "string", 503 "AutomatedUpdateDate": datetime.datetime(2015, 1, 1), 504 } 505 } 506 with patch.object( 507 self.conn, 508 "cancel_elasticsearch_service_software_update", 509 return_value=retval, 510 ): 511 result = boto3_elasticsearch.cancel_elasticsearch_service_software_update( 512 domain_name="testdomain", **CONN_PARAMETERS 513 ) 514 self.assertEqual(result, {"result": True}) 515 516 def test_cancel_elasticsearch_service_software_update_error(self): 517 """ 518 Test that when calling cancel_elasticsearch_service_software_update and 519 boto3 returns an error, it returns {'result': False, 'error': 'the error'}. 520 """ 521 with patch.object( 522 self.conn, 523 "cancel_elasticsearch_service_software_update", 524 side_effect=ClientError( 525 ERROR_CONTENT, "cancel_elasticsearch_service_software_update" 526 ), 527 ): 528 result = boto3_elasticsearch.cancel_elasticsearch_service_software_update( 529 domain_name="testdomain", **CONN_PARAMETERS 530 ) 531 self.assertFalse(result["result"]) 532 self.assertEqual( 533 result.get("error", ""), 534 ERROR_MESSAGE.format( 535 101, "cancel_elasticsearch_service_software_update" 536 ), 537 ) 538 539 def test_delete_elasticsearch_service_role_positive(self): 540 """ 541 Test that when calling delete_elasticsearch_service_role and 542 it is successful, it returns {'result': True}. 543 """ 544 with patch.object( 545 self.conn, "delete_elasticsearch_service_role", return_value=None 546 ): 547 result = boto3_elasticsearch.delete_elasticsearch_service_role( 548 **CONN_PARAMETERS 549 ) 550 self.assertEqual(result, {"result": True}) 551 552 def test_delete_elasticsearch_service_role_error(self): 553 """ 554 Test that when calling delete_elasticsearch_service_role and boto3 returns 555 an error, it returns {'result': False, 'error': 'the error'}. 556 """ 557 with patch.object( 558 self.conn, 559 "delete_elasticsearch_service_role", 560 side_effect=ClientError(ERROR_CONTENT, "delete_elasticsearch_service_role"), 561 ): 562 result = boto3_elasticsearch.delete_elasticsearch_service_role( 563 **CONN_PARAMETERS 564 ) 565 self.assertFalse(result["result"]) 566 self.assertEqual( 567 result.get("error", ""), 568 ERROR_MESSAGE.format(101, "delete_elasticsearch_service_role"), 569 ) 570 571 def test_describe_elasticsearch_domain_config_positive(self): 572 """ 573 Test that when calling describe_elasticsearch_domain_config and 574 it is successful, it returns {'result': True}. 575 """ 576 with patch.object( 577 self.conn, 578 "describe_elasticsearch_domain_config", 579 return_value={"DomainConfig": DOMAIN_RET}, 580 ): 581 self.assertEqual( 582 boto3_elasticsearch.describe_elasticsearch_domain_config( 583 "testdomain", **CONN_PARAMETERS 584 ), 585 {"result": True, "response": DOMAIN_RET}, 586 ) 587 588 def test_describe_elasticsearch_domain_config_error(self): 589 """ 590 Test that when calling describe_elasticsearch_domain_config and boto3 returns 591 an error, it returns {'result': False, 'error': 'the error'}. 592 """ 593 with patch.object( 594 self.conn, 595 "describe_elasticsearch_domain_config", 596 side_effect=ClientError( 597 ERROR_CONTENT, "describe_elasticsearch_domain_config" 598 ), 599 ): 600 result = boto3_elasticsearch.describe_elasticsearch_domain_config( 601 domain_name="testdomain", **CONN_PARAMETERS 602 ) 603 self.assertFalse(result["result"]) 604 self.assertEqual( 605 result.get("error", ""), 606 ERROR_MESSAGE.format(101, "describe_elasticsearch_domain_config"), 607 ) 608 609 def test_describe_elasticsearch_domains_positive(self): 610 """ 611 Test that when calling describe_elasticsearch_domains and it is successful, 612 it returns {'result': True, 'response': some_data}. 613 """ 614 with patch.object( 615 self.conn, 616 "describe_elasticsearch_domains", 617 return_value={"DomainStatusList": [DOMAIN_RET]}, 618 ): 619 self.assertEqual( 620 boto3_elasticsearch.describe_elasticsearch_domains( 621 domain_names=["test_domain"], **CONN_PARAMETERS 622 ), 623 {"result": True, "response": [DOMAIN_RET]}, 624 ) 625 626 def test_describe_elasticsearch_domains_error(self): 627 """ 628 Test that when calling describe_elasticsearch_domains and boto3 returns 629 an error, it returns {'result': False, 'error': 'the error'}. 630 """ 631 with patch.object( 632 self.conn, 633 "describe_elasticsearch_domains", 634 side_effect=ClientError(ERROR_CONTENT, "describe_elasticsearch_domains"), 635 ): 636 result = boto3_elasticsearch.describe_elasticsearch_domains( 637 domain_names=["testdomain"], **CONN_PARAMETERS 638 ) 639 self.assertFalse(result["result"]) 640 self.assertEqual( 641 result.get("error", ""), 642 ERROR_MESSAGE.format(101, "describe_elasticsearch_domains"), 643 ) 644 645 def test_describe_elasticsearch_instance_type_limits_positive(self): 646 """ 647 Test that when calling describe_elasticsearch_instance_type_limits and 648 it succeeds, it returns {'result': True, 'response' some_value}. 649 """ 650 ret_val = { 651 "LimitsByRole": { 652 "string": { 653 "StorageTypes": [ 654 { 655 "StorageTypeName": "string", 656 "StorageSubTypeName": "string", 657 "StorageTypeLimits": [ 658 {"LimitName": "string", "LimitValues": ["string"]} 659 ], 660 } 661 ], 662 "InstanceLimits": { 663 "InstanceCountLimits": { 664 "MinimumInstanceCount": 123, 665 "MaximumInstanceCount": 123, 666 } 667 }, 668 "AdditionalLimits": [ 669 {"LimitName": "string", "LimitValues": ["string"]} 670 ], 671 } 672 } 673 } 674 with patch.object( 675 self.conn, 676 "describe_elasticsearch_instance_type_limits", 677 return_value=ret_val, 678 ): 679 self.assertEqual( 680 boto3_elasticsearch.describe_elasticsearch_instance_type_limits( 681 domain_name="testdomain", 682 instance_type="foo", 683 elasticsearch_version="1.0", 684 **CONN_PARAMETERS 685 ), 686 {"result": True, "response": ret_val["LimitsByRole"]}, 687 ) 688 689 def test_describe_elasticsearch_instance_type_limits_error(self): 690 """ 691 Test that when calling describe_elasticsearch_instance_type_limits and boto3 returns 692 an error, it returns {'result': False, 'error': 'the error'}. 693 """ 694 with patch.object( 695 self.conn, 696 "describe_elasticsearch_instance_type_limits", 697 side_effect=ClientError( 698 ERROR_CONTENT, "describe_elasticsearch_instance_type_limits" 699 ), 700 ): 701 result = boto3_elasticsearch.describe_elasticsearch_instance_type_limits( 702 domain_name="testdomain", 703 instance_type="foo", 704 elasticsearch_version="1.0", 705 **CONN_PARAMETERS 706 ) 707 self.assertFalse(result["result"]) 708 self.assertEqual( 709 result.get("error", ""), 710 ERROR_MESSAGE.format( 711 101, "describe_elasticsearch_instance_type_limits" 712 ), 713 ) 714 715 def test_describe_reserved_elasticsearch_instance_offerings_positive(self): 716 """ 717 Test that when calling describe_reserved_elasticsearch_instance_offerings 718 and it succeeds, it returns {'result': True, 'response': some_value}. 719 """ 720 ret_val = { 721 "NextToken": "string", 722 "ReservedElasticsearchInstanceOfferings": [ 723 { 724 "ReservedElasticsearchInstanceOfferingId": "string", 725 "ElasticsearchInstanceType": "t2.medium.elasticsearch", 726 "Duration": 123, 727 "FixedPrice": 123.0, 728 "UsagePrice": 123.0, 729 "CurrencyCode": "string", 730 "PaymentOption": "NO_UPFRONT", 731 "RecurringCharges": [ 732 { 733 "RecurringChargeAmount": 123.0, 734 "RecurringChargeFrequency": "string", 735 } 736 ], 737 } 738 ], 739 } 740 with patch.object(self.paginator, "paginate", return_value=[ret_val]): 741 self.assertEqual( 742 boto3_elasticsearch.describe_reserved_elasticsearch_instance_offerings( 743 reserved_elasticsearch_instance_offering_id="foo", **CONN_PARAMETERS 744 ), 745 { 746 "result": True, 747 "response": ret_val["ReservedElasticsearchInstanceOfferings"], 748 }, 749 ) 750 751 def test_describe_reserved_elasticsearch_instance_offerings_error(self): 752 """ 753 Test that when calling describe_reserved_elasticsearch_instance_offerings 754 and boto3 returns an error, it returns {'result': False, 'error': 'the error'}. 755 """ 756 with patch.object( 757 self.paginator, 758 "paginate", 759 side_effect=ClientError( 760 ERROR_CONTENT, "describe_reserved_elasticsearch_instance_offerings" 761 ), 762 ): 763 result = ( 764 boto3_elasticsearch.describe_reserved_elasticsearch_instance_offerings( 765 reserved_elasticsearch_instance_offering_id="foo", **CONN_PARAMETERS 766 ) 767 ) 768 self.assertFalse(result["result"]) 769 self.assertEqual( 770 result.get("error", ""), 771 ERROR_MESSAGE.format( 772 101, "describe_reserved_elasticsearch_instance_offerings" 773 ), 774 ) 775 776 def test_describe_reserved_elasticsearch_instances_positive(self): 777 """ 778 Test that when calling describe_reserved_elasticsearch_instances and it 779 succeeds, it returns {'result': True, 'response': some_value}. 780 """ 781 ret_val = { 782 "NextToken": "string", 783 "ReservedElasticsearchInstances": [ 784 { 785 "ReservationName": "string", 786 "ReservedElasticsearchInstanceId": "string", 787 "ReservedElasticsearchInstanceOfferingId": "string", 788 "ElasticsearchInstanceType": "t2.medium.elasticsearch", 789 "StartTime": datetime.datetime(2015, 1, 1), 790 "Duration": 123, 791 "FixedPrice": 123.0, 792 "UsagePrice": 123.0, 793 "CurrencyCode": "string", 794 "ElasticsearchInstanceCount": 123, 795 "State": "string", 796 "PaymentOption": "ALL_UPFRONT", 797 "RecurringCharges": [ 798 { 799 "RecurringChargeAmount": 123.0, 800 "RecurringChargeFrequency": "string", 801 }, 802 ], 803 }, 804 ], 805 } 806 with patch.object(self.paginator, "paginate", return_value=[ret_val]): 807 self.assertEqual( 808 boto3_elasticsearch.describe_reserved_elasticsearch_instances( 809 reserved_elasticsearch_instance_id="foo", **CONN_PARAMETERS 810 ), 811 {"result": True, "response": ret_val["ReservedElasticsearchInstances"]}, 812 ) 813 814 def test_describe_reserved_elasticsearch_instances_error(self): 815 """ 816 Test that when calling describe_reserved_elasticsearch_instances and boto3 817 returns an error, it returns {'result': False, 'error': 'the error'}. 818 """ 819 with patch.object( 820 self.paginator, 821 "paginate", 822 side_effect=ClientError( 823 ERROR_CONTENT, "describe_reserved_elasticsearch_instances" 824 ), 825 ): 826 result = boto3_elasticsearch.describe_reserved_elasticsearch_instances( 827 reserved_elasticsearch_instance_id="foo", **CONN_PARAMETERS 828 ) 829 self.assertFalse(result["result"]) 830 self.assertEqual( 831 result.get("error", ""), 832 ERROR_MESSAGE.format(101, "describe_reserved_elasticsearch_instances"), 833 ) 834 835 def test_get_compatible_elasticsearch_versions_positive(self): 836 """ 837 Test that when calling get_compatible_elasticsearch_versions and it 838 succeeds, it returns {'result': True, 'response': some_value}. 839 """ 840 ret_val = { 841 "CompatibleElasticsearchVersions": [ 842 {"SourceVersion": "string", "TargetVersions": ["string"]} 843 ] 844 } 845 with patch.object( 846 self.conn, "get_compatible_elasticsearch_versions", return_value=ret_val 847 ): 848 self.assertEqual( 849 boto3_elasticsearch.get_compatible_elasticsearch_versions( 850 domain_name="testdomain", **CONN_PARAMETERS 851 ), 852 { 853 "result": True, 854 "response": ret_val["CompatibleElasticsearchVersions"], 855 }, 856 ) 857 858 def test_get_compatible_elasticsearch_versions_error(self): 859 """ 860 Test that when calling get_compatible_elasticsearch_versions and boto3 861 returns an error, it returns {'result': False, 'error': 'the error'}. 862 """ 863 with patch.object( 864 self.conn, 865 "get_compatible_elasticsearch_versions", 866 side_effect=ClientError( 867 ERROR_CONTENT, "get_compatible_elasticsearch_versions" 868 ), 869 ): 870 result = boto3_elasticsearch.get_compatible_elasticsearch_versions( 871 domain_name="testdomain", **CONN_PARAMETERS 872 ) 873 self.assertFalse(result["result"]) 874 self.assertEqual( 875 result.get("error", ""), 876 ERROR_MESSAGE.format(101, "get_compatible_elasticsearch_versions"), 877 ) 878 879 def test_get_upgrade_history_positive(self): 880 """ 881 Test that when calling get_upgrade_history and it 882 succeeds, it returns {'result': True, 'response': some_value}. 883 """ 884 ret_val = { 885 "UpgradeHistories": [ 886 { 887 "UpgradeName": "string", 888 "StartTimestamp": datetime.datetime(2015, 1, 1), 889 "UpgradeStatus": "IN_PROGRESS", 890 "StepsList": [ 891 { 892 "UpgradeStep": "PRE_UPGRADE_CHECK", 893 "UpgradeStepStatus": "IN_PROGRESS", 894 "Issues": ["string"], 895 "ProgressPercent": 123.0, 896 } 897 ], 898 } 899 ], 900 "NextToken": "string", 901 } 902 with patch.object(self.paginator, "paginate", return_value=[ret_val]): 903 self.assertEqual( 904 boto3_elasticsearch.get_upgrade_history( 905 domain_name="testdomain", **CONN_PARAMETERS 906 ), 907 {"result": True, "response": ret_val["UpgradeHistories"]}, 908 ) 909 910 def test_get_upgrade_history_error(self): 911 """ 912 Test that when calling get_upgrade_history and boto3 913 returns an error, it returns {'result': False, 'error': 'the error'}. 914 """ 915 with patch.object( 916 self.paginator, 917 "paginate", 918 side_effect=ClientError(ERROR_CONTENT, "get_upgrade_history"), 919 ): 920 result = boto3_elasticsearch.get_upgrade_history( 921 domain_name="testdomain", **CONN_PARAMETERS 922 ) 923 self.assertFalse(result["result"]) 924 self.assertEqual( 925 result.get("error", ""), 926 ERROR_MESSAGE.format(101, "get_upgrade_history"), 927 ) 928 929 def test_get_upgrade_status_positive(self): 930 """ 931 Test that when calling get_upgrade_status and it 932 succeeds, it returns {'result': True, 'response': some_value}. 933 """ 934 ret_val = { 935 "UpgradeStep": "PRE_UPGRADE_CHECK", 936 "StepStatus": "IN_PROGRESS", 937 "UpgradeName": "string", 938 "ResponseMetadata": None, 939 } 940 with patch.object(self.conn, "get_upgrade_status", return_value=ret_val): 941 self.assertEqual( 942 boto3_elasticsearch.get_upgrade_status( 943 domain_name="testdomain", **CONN_PARAMETERS 944 ), 945 {"result": True, "response": ret_val}, 946 ) 947 948 def test_get_upgrade_status_error(self): 949 """ 950 Test that when calling get_upgrade_status and boto3 951 returns an error, it returns {'result': False, 'error': 'the error'}. 952 """ 953 with patch.object( 954 self.conn, 955 "get_upgrade_status", 956 side_effect=ClientError(ERROR_CONTENT, "get_upgrade_status"), 957 ): 958 result = boto3_elasticsearch.get_upgrade_status( 959 domain_name="testdomain", **CONN_PARAMETERS 960 ) 961 self.assertFalse(result["result"]) 962 self.assertEqual( 963 result.get("error", ""), ERROR_MESSAGE.format(101, "get_upgrade_status") 964 ) 965 966 def test_list_domain_names_positive(self): 967 """ 968 Test that when calling list_domain_names and it 969 succeeds, it returns {'result': True, 'response': some_value}. 970 """ 971 ret_val = {"DomainNames": [{"DomainName": "string"}]} 972 with patch.object(self.conn, "list_domain_names", return_value=ret_val): 973 self.assertEqual( 974 boto3_elasticsearch.list_domain_names(**CONN_PARAMETERS), 975 { 976 "result": True, 977 "response": [item["DomainName"] for item in ret_val["DomainNames"]], 978 }, 979 ) 980 981 def test_list_domain_names_error(self): 982 """ 983 Test that when calling list_domain_names and boto3 984 returns an error, it returns {'result': False, 'error': 'the error'}. 985 """ 986 with patch.object( 987 self.conn, 988 "list_domain_names", 989 side_effect=ClientError(ERROR_CONTENT, "list_domain_names"), 990 ): 991 result = boto3_elasticsearch.list_domain_names(**CONN_PARAMETERS) 992 self.assertFalse(result["result"]) 993 self.assertEqual( 994 result.get("error", ""), ERROR_MESSAGE.format(101, "list_domain_names") 995 ) 996 997 def test_list_elasticsearch_instance_types_positive(self): 998 """ 999 Test that when calling list_elasticsearch_instance_types and it 1000 succeeds, it returns {'result': True, 'response': some_value}. 1001 """ 1002 ret_val = { 1003 "ElasticsearchInstanceTypes": [ 1004 "m3.medium.elasticsearch", 1005 "m3.large.elasticsearch", 1006 "m3.xlarge.elasticsearch", 1007 "m3.2xlarge.elasticsearch", 1008 "m4.large.elasticsearch", 1009 "m4.xlarge.elasticsearch", 1010 "m4.2xlarge.elasticsearch", 1011 "m4.4xlarge.elasticsearch", 1012 "m4.10xlarge.elasticsearch", 1013 "t2.micro.elasticsearch", 1014 "t2.small.elasticsearch", 1015 "t2.medium.elasticsearch", 1016 "r3.large.elasticsearch", 1017 "r3.xlarge.elasticsearch", 1018 "r3.2xlarge.elasticsearch", 1019 "r3.4xlarge.elasticsearch", 1020 "r3.8xlarge.elasticsearch", 1021 "i2.xlarge.elasticsearch", 1022 "i2.2xlarge.elasticsearch", 1023 "d2.xlarge.elasticsearch", 1024 "d2.2xlarge.elasticsearch", 1025 "d2.4xlarge.elasticsearch", 1026 "d2.8xlarge.elasticsearch", 1027 "c4.large.elasticsearch", 1028 "c4.xlarge.elasticsearch", 1029 "c4.2xlarge.elasticsearch", 1030 "c4.4xlarge.elasticsearch", 1031 "c4.8xlarge.elasticsearch", 1032 "r4.large.elasticsearch", 1033 "r4.xlarge.elasticsearch", 1034 "r4.2xlarge.elasticsearch", 1035 "r4.4xlarge.elasticsearch", 1036 "r4.8xlarge.elasticsearch", 1037 "r4.16xlarge.elasticsearch", 1038 "i3.large.elasticsearch", 1039 "i3.xlarge.elasticsearch", 1040 "i3.2xlarge.elasticsearch", 1041 "i3.4xlarge.elasticsearch", 1042 "i3.8xlarge.elasticsearch", 1043 "i3.16xlarge.elasticsearch", 1044 ], 1045 "NextToken": "string", 1046 } 1047 with patch.object(self.paginator, "paginate", return_value=[ret_val]): 1048 self.assertEqual( 1049 boto3_elasticsearch.list_elasticsearch_instance_types( 1050 elasticsearch_version="1.0", **CONN_PARAMETERS 1051 ), 1052 {"result": True, "response": ret_val["ElasticsearchInstanceTypes"]}, 1053 ) 1054 1055 def test_list_elasticsearch_instance_types_error(self): 1056 """ 1057 Test that when calling list_elasticsearch_instance_types and boto3 1058 returns an error, it returns {'result': False, 'error': 'the error'}. 1059 """ 1060 with patch.object( 1061 self.paginator, 1062 "paginate", 1063 side_effect=ClientError(ERROR_CONTENT, "list_elasticsearch_instance_types"), 1064 ): 1065 result = boto3_elasticsearch.list_elasticsearch_instance_types( 1066 elasticsearch_version="1.0", **CONN_PARAMETERS 1067 ) 1068 self.assertFalse(result["result"]) 1069 self.assertEqual( 1070 result.get("error", ""), 1071 ERROR_MESSAGE.format(101, "list_elasticsearch_instance_types"), 1072 ) 1073 1074 def test_list_elasticsearch_versions_positive(self): 1075 """ 1076 Test that when calling list_elasticsearch_versions and it 1077 succeeds, it returns {'result': True, 'response': some_value}. 1078 """ 1079 ret_val = {"ElasticsearchVersions": ["string"], "NextToken": "string"} 1080 with patch.object(self.paginator, "paginate", return_value=[ret_val]): 1081 self.assertEqual( 1082 boto3_elasticsearch.list_elasticsearch_versions(**CONN_PARAMETERS), 1083 {"result": True, "response": ret_val["ElasticsearchVersions"]}, 1084 ) 1085 1086 def test_list_elasticsearch_versions_error(self): 1087 """ 1088 Test that when calling list_elasticsearch_versions and boto3 1089 returns an error, it returns {'result': False, 'error': 'the error'}. 1090 """ 1091 with patch.object( 1092 self.paginator, 1093 "paginate", 1094 side_effect=ClientError(ERROR_CONTENT, "list_elasticsearch_versions"), 1095 ): 1096 result = boto3_elasticsearch.list_elasticsearch_versions(**CONN_PARAMETERS) 1097 self.assertFalse(result["result"]) 1098 self.assertEqual( 1099 result.get("error", ""), 1100 ERROR_MESSAGE.format(101, "list_elasticsearch_versions"), 1101 ) 1102 1103 def test_purchase_reserved_elasticsearch_instance_offering_positive(self): 1104 """ 1105 Test that when calling purchase_reserved_elasticsearch_instance_offering and it 1106 succeeds, it returns {'result': True, 'response': some_value}. 1107 """ 1108 ret_val = { 1109 "ReservedElasticsearchInstanceId": "string", 1110 "ReservationName": "string", 1111 } 1112 with patch.object( 1113 self.conn, 1114 "purchase_reserved_elasticsearch_instance_offering", 1115 return_value=ret_val, 1116 ): 1117 self.assertEqual( 1118 boto3_elasticsearch.purchase_reserved_elasticsearch_instance_offering( 1119 reserved_elasticsearch_instance_offering_id="foo", 1120 reservation_name="bar", 1121 **CONN_PARAMETERS 1122 ), 1123 {"result": True, "response": ret_val}, 1124 ) 1125 1126 def test_purchase_reserved_elasticsearch_instance_offering_error(self): 1127 """ 1128 Test that when calling purchase_reserved_elasticsearch_instance_offering and boto3 1129 returns an error, it returns {'result': False, 'error': 'the error'}. 1130 """ 1131 with patch.object( 1132 self.conn, 1133 "purchase_reserved_elasticsearch_instance_offering", 1134 side_effect=ClientError( 1135 ERROR_CONTENT, "purchase_reserved_elasticsearch_instance_offering" 1136 ), 1137 ): 1138 result = ( 1139 boto3_elasticsearch.purchase_reserved_elasticsearch_instance_offering( 1140 reserved_elasticsearch_instance_offering_id="foo", 1141 reservation_name="bar", 1142 **CONN_PARAMETERS 1143 ) 1144 ) 1145 self.assertFalse(result["result"]) 1146 self.assertEqual( 1147 result.get("error", ""), 1148 ERROR_MESSAGE.format( 1149 101, "purchase_reserved_elasticsearch_instance_offering" 1150 ), 1151 ) 1152 1153 def test_start_elasticsearch_service_software_update_positive(self): 1154 """ 1155 Test that when calling start_elasticsearch_service_software_update and it 1156 succeeds, it returns {'result': True, 'response': some_value}. 1157 """ 1158 ret_val = { 1159 "ServiceSoftwareOptions": { 1160 "CurrentVersion": "string", 1161 "NewVersion": "string", 1162 "UpdateAvailable": True, 1163 "Cancellable": True, 1164 "UpdateStatus": "PENDING_UPDATE", 1165 "Description": "string", 1166 "AutomatedUpdateDate": datetime.datetime(2015, 1, 1), 1167 } 1168 } 1169 with patch.object( 1170 self.conn, 1171 "start_elasticsearch_service_software_update", 1172 return_value=ret_val, 1173 ): 1174 self.assertEqual( 1175 boto3_elasticsearch.start_elasticsearch_service_software_update( 1176 domain_name="testdomain", **CONN_PARAMETERS 1177 ), 1178 {"result": True, "response": ret_val["ServiceSoftwareOptions"]}, 1179 ) 1180 1181 def test_start_elasticsearch_service_software_update_error(self): 1182 """ 1183 Test that when calling start_elasticsearch_service_software_update and boto3 1184 returns an error, it returns {'result': False, 'error': 'the error'}. 1185 """ 1186 with patch.object( 1187 self.conn, 1188 "start_elasticsearch_service_software_update", 1189 side_effect=ClientError( 1190 ERROR_CONTENT, "start_elasticsearch_service_software_update" 1191 ), 1192 ): 1193 result = boto3_elasticsearch.start_elasticsearch_service_software_update( 1194 domain_name="testdomain", **CONN_PARAMETERS 1195 ) 1196 self.assertFalse(result["result"]) 1197 self.assertEqual( 1198 result.get("error", ""), 1199 ERROR_MESSAGE.format( 1200 101, "start_elasticsearch_service_software_update" 1201 ), 1202 ) 1203 1204 def test_upgrade_elasticsearch_domain_positive(self): 1205 """ 1206 Test that when calling upgrade_elasticsearch_domain and it 1207 succeeds, it returns {'result': True, 'response': some_value}. 1208 """ 1209 ret_val = { 1210 "DomainName": "string", 1211 "TargetVersion": "string", 1212 "PerformCheckOnly": True, 1213 } 1214 with patch.object( 1215 self.conn, "upgrade_elasticsearch_domain", return_value=ret_val 1216 ): 1217 self.assertEqual( 1218 boto3_elasticsearch.upgrade_elasticsearch_domain( 1219 domain_name="testdomain", target_version="1.1", **CONN_PARAMETERS 1220 ), 1221 {"result": True, "response": ret_val}, 1222 ) 1223 1224 def test_upgrade_elasticsearch_domain_error(self): 1225 """ 1226 Test that when calling upgrade_elasticsearch_domain and boto3 1227 returns an error, it returns {'result': False, 'error': 'the error'}. 1228 """ 1229 with patch.object( 1230 self.conn, 1231 "upgrade_elasticsearch_domain", 1232 side_effect=ClientError(ERROR_CONTENT, "upgrade_elasticsearch_domain"), 1233 ): 1234 result = boto3_elasticsearch.upgrade_elasticsearch_domain( 1235 domain_name="testdomain", target_version="1.1", **CONN_PARAMETERS 1236 ) 1237 self.assertFalse(result["result"]) 1238 self.assertEqual( 1239 result.get("error", ""), 1240 ERROR_MESSAGE.format(101, "upgrade_elasticsearch_domain"), 1241 ) 1242