salt/tests/unit/states/test_boto_elasticsearch_domain.py

206 lines
8.3 KiB
Python
Raw Normal View History

2016-03-10 15:55:56 +00:00
# -*- coding: utf-8 -*-
# Import Python libs
from __future__ import absolute_import
Remove repr formatting flag in places where it is used solely for quoting (#34183) * salt/cloud/__init__.py: remove repr formatting * salt/cloud/clouds/azurearm.py: remove repr formatting * salt/cloud/clouds/ec2.py: remove repr formatting * salt/cloud/clouds/profitbricks.py: remove repr formatting * salt/loader.py: remove repr formatting * salt/modules/win_file.py: remove repr formatting * salt/modules/zypper.py: remove repr formatting * salt/pillar/consul_pillar.py: remove repr formatting * salt/renderers/pyobjects.py: remove repr formatting * salt/returners/sentry_return.py: remove repr formatting * salt/states/bower.py: remove repr formatting * salt/states/cabal.py: remove repr formatting * salt/states/cmd.py: remove repr formatting * salt/states/composer.py: remove repr formatting * salt/states/win_network.py: remove repr formatting * salt/states/eselect.py: remove repr formatting * salt/states/file.py: remove repr formatting * salt/states/htpasswd.py: remove repr formatting * salt/states/memcached.py: remove repr formatting * salt/states/npm.py: remove repr formatting * salt/states/pip_state.py: remove repr formatting * salt/states/pkg.py: remove repr formatting * salt/states/pkgrepo.py: remove repr formatting * salt/states/supervisord.py: remove repr formatting * salt/states/timezone.py: remove repr formatting * salt/states/virtualenv_mod.py: remove repr formatting * salt/states/dockerio.py: remove repr formatting * salt/states/win_system.py: remove repr formatting * salt/utils/nb_popen.py: remove repr formatting * salt/utils/cloud.py: remove repr formatting * Add pylint disable due to legit usage of repr flag See https://github.com/saltstack/salt-pylint/pull/6 * Fix composer tests These tests needed to be updated because quoting was changed in the state module in 9dc9146. There was an unnecessary !r used for the exception class there, which means that instead of the exception class being passed through the formatter and coming out with the equivalent value of err.__str__(), we get a repr'ed instance of the exception class (i.e. SaltException('',)) in the state output. The unit test was asserting that we have that repr'ed instance of SaltException in the output, a case of writing the test to confirm the badly-conceived output in the state. This has also been corrected. * salt/cloud/clouds/azurearm.py: lint fixes * salt/modules/boto_s3_bucket.py: lint fixes * salt/modules/minion.py: lint fixes * salt/modules/reg.py: lint fixes * salt/modules/testinframod.py: lint fixes * salt/modules/win_iis.py: lint fixes * salt/pillar/csvpillar.py: lint fixes * salt/utils/win_functions.py: lint fixes * salt/states/nxos.py: lint fixes * salt/returners/mongo_future_return.py: lint fixes * tests/integration/__init__.py: lint fixes * tests/unit/context_test.py: lint fixes * tests/integration/states/file.py: lint fixes * tests/integration/utils/test_reactor.py: lint fixes * tests/integration/utils/testprogram.py: lint fixes * tests/unit/__init__.py: lint fixes * tests/integration/shell/minion.py: lint fixes * tests/unit/modules/boto_apigateway_test.py: lint fixes * tests/unit/modules/boto_cognitoidentity_test.py: lint fixes * tests/unit/modules/boto_elasticsearch_domain_test.py: lint fixes * tests/unit/modules/k8s_test.py: lint fixes * tests/unit/modules/reg_win_test.py: lint fixes * tests/unit/states/boto_apigateway_test.py: lint fixes * tests/unit/states/boto_cognitoidentity_test.py: lint fixes * tests/unit/states/boto_elasticsearch_domain_test.py: lint fixes
2016-06-29 20:30:18 +00:00
import logging
import random
import string
2016-03-10 15:55:56 +00:00
# Import Salt Testing libs
2017-02-19 22:28:46 +00:00
from tests.support.mixins import LoaderModuleMockMixin
from tests.support.unit import skipIf, TestCase
2017-02-19 22:28:46 +00:00
from tests.support.mock import MagicMock, NO_MOCK, NO_MOCK_REASON, patch
2016-03-10 15:55:56 +00:00
# Import Salt libs
Use explicit unicode strings + break up salt.utils This PR is part of what will be an ongoing effort to use explicit unicode strings in Salt. Because Python 3 does not suport Python 2's raw unicode string syntax (i.e. `ur'\d+'`), we must use `salt.utils.locales.sdecode()` to ensure that the raw string is unicode. However, because of how `salt/utils/__init__.py` has evolved into the hulking monstrosity it is today, this means importing a large module in places where it is not needed, which could negatively impact performance. For this reason, this PR also breaks out some of the functions from `salt/utils/__init__.py` into new/existing modules under `salt/utils/`. The long term goal will be that the modules within this directory do not depend on importing `salt.utils`. A summary of the changes in this PR is as follows: * Moves the following functions from `salt.utils` to new locations (including a deprecation warning if invoked from `salt.utils`): `to_bytes`, `to_str`, `to_unicode`, `str_to_num`, `is_quoted`, `dequote`, `is_hex`, `is_bin_str`, `rand_string`, `contains_whitespace`, `clean_kwargs`, `invalid_kwargs`, `which`, `which_bin`, `path_join`, `shlex_split`, `rand_str`, `is_windows`, `is_proxy`, `is_linux`, `is_darwin`, `is_sunos`, `is_smartos`, `is_smartos_globalzone`, `is_smartos_zone`, `is_freebsd`, `is_netbsd`, `is_openbsd`, `is_aix` * Moves the functions already deprecated by @rallytime to the bottom of `salt/utils/__init__.py` for better organization, so we can keep the deprecated ones separate from the ones yet to be deprecated as we continue to break up `salt.utils` * Updates `salt/*.py` and all files under `salt/client/` to use explicit unicode string literals. * Gets rid of implicit imports of `salt.utils` (e.g. `from salt.utils import foo` becomes `import salt.utils.foo as foo`). * Renames the `test.rand_str` function to `test.random_hash` to more accurately reflect what it does * Modifies `salt.utils.stringutils.random()` (née `salt.utils.rand_string()`) such that it returns a string matching the passed size. Previously this function would get `size` bytes from `os.urandom()`, base64-encode it, and return the result, which would in most cases not be equal to the passed size.
2017-07-25 01:47:15 +00:00
from salt.ext import six
2016-03-10 15:55:56 +00:00
import salt.loader
from salt.utils.versions import LooseVersion
2017-02-19 22:28:46 +00:00
import salt.states.boto_elasticsearch_domain as boto_elasticsearch_domain
# Import test suite libs
2016-03-10 15:55:56 +00:00
# pylint: disable=import-error,no-name-in-module,unused-import
from tests.unit.modules.test_boto_elasticsearch_domain import BotoElasticsearchDomainTestCaseMixin
2016-03-10 15:55:56 +00:00
# Import 3rd-party libs
2017-02-19 22:28:46 +00:00
from salt.ext.six.moves import range # pylint: disable=import-error,redefined-builtin
2016-03-10 15:55:56 +00:00
try:
import boto
import boto3
from botocore.exceptions import ClientError
HAS_BOTO = True
except ImportError:
HAS_BOTO = False
# pylint: enable=import-error,no-name-in-module,unused-import
# the boto_elasticsearch_domain module relies on the connect_to_region() method
# which was added in boto 2.8.0
# https://github.com/boto/boto/commit/33ac26b416fbb48a60602542b4ce15dcc7029f12
required_boto3_version = '1.2.1'
log = logging.getLogger(__name__)
def _has_required_boto():
'''
Returns True/False boolean depending on if Boto is installed and correct
version.
'''
if not HAS_BOTO:
return False
elif LooseVersion(boto3.__version__) < LooseVersion(required_boto3_version):
return False
else:
return True
if _has_required_boto():
region = 'us-east-1'
access_key = 'GKTADJGHEIQSXMKKRBJ08H'
secret_key = 'askdjghsdfjkghWupUjasdflkdfklgjsdfjajkghs'
conn_parameters = {'region': region, 'key': access_key, 'keyid': secret_key, 'profile': {}}
error_message = 'An error occurred (101) when calling the {0} operation: Test-defined error'
not_found_error = ClientError({
'Error': {
'Code': 'ResourceNotFoundException',
'Message': "Test-defined error"
}
}, 'msg')
error_content = {
'Error': {
'Code': 101,
'Message': "Test-defined error"
}
}
domain_ret = dict(DomainName='testdomain',
ElasticsearchClusterConfig={},
EBSOptions={},
AccessPolicies={},
SnapshotOptions={},
AdvancedOptions={},
ElasticsearchVersion='1.5',
)
2016-03-10 15:55:56 +00:00
2017-02-19 22:28:46 +00:00
class BotoElasticsearchDomainStateTestCaseBase(TestCase, LoaderModuleMockMixin):
2016-03-10 15:55:56 +00:00
conn = None
def setup_loader_modules(self):
ctx = {}
utils = salt.loader.utils(self.opts, whitelist=['boto3'], context=ctx)
serializers = salt.loader.serializers(self.opts)
self.funcs = funcs = salt.loader.minion_mods(self.opts, context=ctx, utils=utils, whitelist=['boto_elasticsearch_domain'])
self.salt_states = salt.loader.states(opts=self.opts, functions=funcs, utils=utils, whitelist=['boto_elasticsearch_domain'],
serializers=serializers)
return {
boto_elasticsearch_domain: {
'__opts__': self.opts,
'__salt__': funcs,
'__utils__': utils,
'__states__': self.salt_states,
'__serializers__': serializers,
}
}
2017-02-19 22:28:46 +00:00
@classmethod
def setUpClass(cls):
cls.opts = salt.config.DEFAULT_MINION_OPTS
cls.opts['grains'] = salt.loader.grains(cls.opts)
@classmethod
def tearDownClass(cls):
del cls.opts
2016-03-10 15:55:56 +00:00
# Set up MagicMock to replace the boto3 session
def setUp(self):
2017-02-19 22:28:46 +00:00
self.addCleanup(delattr, self, 'funcs')
self.addCleanup(delattr, self, 'salt_states')
# Set up MagicMock to replace the boto3 session
# connections keep getting cached from prior tests, can't find the
# correct context object to clear it. So randomize the cache key, to prevent any
# cache hits
conn_parameters['key'] = ''.join(random.choice(string.ascii_lowercase + string.digits) for _ in range(50))
2016-03-10 15:55:56 +00:00
self.patcher = patch('boto3.session.Session')
self.addCleanup(self.patcher.stop)
2017-02-19 22:28:46 +00:00
self.addCleanup(delattr, self, 'patcher')
2016-03-10 15:55:56 +00:00
mock_session = self.patcher.start()
session_instance = mock_session.return_value
self.conn = MagicMock()
2017-02-19 22:28:46 +00:00
self.addCleanup(delattr, self, 'conn')
2016-03-10 15:55:56 +00:00
session_instance.client.return_value = self.conn
@skipIf(HAS_BOTO is False, 'The boto module must be installed.')
@skipIf(_has_required_boto() is False, 'The boto3 module must be greater than'
' or equal to version {0}'
.format(required_boto3_version))
@skipIf(NO_MOCK, NO_MOCK_REASON)
class BotoElasticsearchDomainTestCase(BotoElasticsearchDomainStateTestCaseBase, BotoElasticsearchDomainTestCaseMixin):
'''
TestCase for salt.modules.boto_elasticsearch_domain state.module
'''
def test_present_when_domain_does_not_exist(self):
'''
Tests present on a domain that does not exist.
'''
self.conn.describe_elasticsearch_domain.side_effect = not_found_error
self.conn.describe_elasticsearch_domain_config.return_value = {'DomainConfig': domain_ret}
self.conn.create_elasticsearch_domain.return_value = {'DomainStatus': domain_ret}
2017-02-19 22:28:46 +00:00
result = self.salt_states['boto_elasticsearch_domain.present'](
2016-03-10 15:55:56 +00:00
'domain present',
**domain_ret)
self.assertTrue(result['result'])
self.assertEqual(result['changes']['new']['domain']['ElasticsearchClusterConfig'], None)
def test_present_when_domain_exists(self):
self.conn.describe_elasticsearch_domain.return_value = {'DomainStatus': domain_ret}
cfg = {}
for k, v in six.iteritems(domain_ret):
2016-03-10 15:55:56 +00:00
cfg[k] = {'Options': v}
cfg['AccessPolicies'] = {'Options': '{"a": "b"}'}
self.conn.describe_elasticsearch_domain_config.return_value = {'DomainConfig': cfg}
self.conn.update_elasticsearch_domain_config.return_value = {'DomainConfig': cfg}
2017-02-19 22:28:46 +00:00
result = self.salt_states['boto_elasticsearch_domain.present'](
2016-03-10 15:55:56 +00:00
'domain present',
**domain_ret)
self.assertTrue(result['result'])
self.assertEqual(result['changes'], {'new': {'AccessPolicies': {}}, 'old': {'AccessPolicies': {u'a': u'b'}}})
def test_present_with_failure(self):
self.conn.describe_elasticsearch_domain.side_effect = not_found_error
self.conn.describe_elasticsearch_domain_config.return_value = {'DomainConfig': domain_ret}
self.conn.create_elasticsearch_domain.side_effect = ClientError(error_content, 'create_domain')
2017-02-19 22:28:46 +00:00
result = self.salt_states['boto_elasticsearch_domain.present'](
2016-03-10 15:55:56 +00:00
'domain present',
**domain_ret)
self.assertFalse(result['result'])
self.assertTrue('An error occurred' in result['comment'])
def test_absent_when_domain_does_not_exist(self):
'''
Tests absent on a domain that does not exist.
'''
self.conn.describe_elasticsearch_domain.side_effect = not_found_error
2017-02-19 22:28:46 +00:00
result = self.salt_states['boto_elasticsearch_domain.absent']('test', 'mydomain')
2016-03-10 15:55:56 +00:00
self.assertTrue(result['result'])
self.assertEqual(result['changes'], {})
def test_absent_when_domain_exists(self):
self.conn.describe_elasticsearch_domain.return_value = {'DomainStatus': domain_ret}
self.conn.describe_elasticsearch_domain_config.return_value = {'DomainConfig': domain_ret}
2017-02-19 22:28:46 +00:00
result = self.salt_states['boto_elasticsearch_domain.absent']('test', domain_ret['DomainName'])
2016-03-10 15:55:56 +00:00
self.assertTrue(result['result'])
self.assertEqual(result['changes']['new']['domain'], None)
def test_absent_with_failure(self):
self.conn.describe_elasticsearch_domain.return_value = {'DomainStatus': domain_ret}
self.conn.describe_elasticsearch_domain_config.return_value = {'DomainConfig': domain_ret}
self.conn.delete_elasticsearch_domain.side_effect = ClientError(error_content, 'delete_domain')
2017-02-19 22:28:46 +00:00
result = self.salt_states['boto_elasticsearch_domain.absent']('test', domain_ret['DomainName'])
2016-03-10 15:55:56 +00:00
self.assertFalse(result['result'])
self.assertTrue('An error occurred' in result['comment'])