chore: linting and basic unit tests

This commit is contained in:
Mauro Torrez 2020-10-05 08:51:37 -03:00
parent 82f0ae24ef
commit 6a5b430597
2 changed files with 194 additions and 61 deletions

137
test_wildfly.py Normal file
View File

@ -0,0 +1,137 @@
'''
Unit tests for wildfly module
'''
import unittest
from six import string_types
from wildfly import WildflyPath, format_attr, config_query, wildfly_batch
class TestWildflyPath(unittest.TestCase):
'''
Tests for WildflyPath class.
'''
def setUp(self):
test_input = r'''
/subsystem=undertow/server=default-server/host=default-host
/location=\/myapp\/multimedia
'''
self.instance = WildflyPath(test_input)
def test_wildfly_path_str(self):
self.assertEqual(
str(self.instance),
r'/subsystem=undertow/server=default-server/host=default-host'
r'/location=\/myapp\/multimedia'
)
def test_wildfly_path_len(self):
self.assertEqual(len(self.instance), 8)
def test_wildfly_path_lpop(self):
self.instance.lpop()
self.instance.lpop()
self.assertEqual(
str(self.instance),
r'/server=default-server/host=default-host'
r'/location=\/myapp\/multimedia'
)
class TestFormatAttr(unittest.TestCase):
'''
Tests for format_attr function.
'''
def test_format_attr_return_type(self):
for input_ in (None, True, 1234, 1234.5, ['123',243],
'hello', {'a': 123}):
self.assertIsInstance(format_attr(input_), string_types)
def test_format_attr_true(self):
self.assertEqual(format_attr(True), 'True')
def test_format_attr_false(self):
self.assertEqual(format_attr(False), 'False')
def test_format_attr_none(self):
self.assertEqual(format_attr(None), 'undefined')
def test_format_attr_float(self):
self.assertEqual(format_attr(1234.5), '1234.500000')
def test_format_attr_int(self):
self.assertEqual(format_attr(1234), '1234')
def test_format_attr_bigint(self):
self.assertEqual(format_attr(1e10), '10000000000.000000')
def test_format_attr_string(self):
self.assertEqual(format_attr('hello'), '"hello"')
def test_format_attr_stripquotes(self):
self.assertEqual(format_attr('"hello"'), '"hello"')
class TestConfigQueryFunction(unittest.TestCase):
'''
Tests for config_query function.
'''
def setUp(self):
self.config0 = {'core-service': {'management': {'security-realm': {
'ManagementRealm': {'plug-in': None, 'server-identity': None}}}}}
self.path0 = ('/core-service=management/security-realm=ManagementRealm'
'/server-identity=ssl')
self.config1 = {'core-service': {'management': {'security-realm': {
'ManagementRealm': {'server-identity': {'ssl': {
'alias': 'wildfly',
'key-password': 'changeit',
'keystore-password': 'changeit',
'keystore-path': 'server-admin.keystore',
'keystore-relative-to': 'jboss.server.config.dir',
}}}}}}}
def test_config_query_none(self):
self.assertEqual(config_query(self.config0, self.path0), None)
class TestBatch(unittest.TestCase):
'''
Tests for wildfly_batch function.
'''
def setUp(self):
self.config0 = {'core-service': {'management': {'security-realm': {
'ManagementRealm': {'plug-in': None, 'server-identity': None}}}}}
self.path0 = ('/core-service=management/security-realm=ManagementRealm'
'/server-identity=ssl')
self.attrs0 = {
'alias': 'wildfly',
'key-password': 'changeit',
'keystore-password': 'changeit',
'keystore-path': 'server-admin.keystore',
'keystore-relative-to': 'jboss.server.config.dir',
}
self.batch0 = '''\
/core-service=management/security-realm=ManagementRealm\
/server-identity=ssl:add(alias="wildfly",key-password="changeit",\
keystore-password="changeit",keystore-path="server-admin.keystore",\
keystore-relative-to="jboss.server.config.dir")
'''
self.config1 = {'core-service': {'management': {'security-realm': {
'ManagementRealm': {'plug-in': None, 'server-identity': {'ssl': {
'alias': 'wildfly',
'key-password': 'changeit',
'keystore-password': 'changeit',
'keystore-path': 'server-admin.keystore',
'keystore-relative-to': 'jboss.server.config.dir',
}}}}}}}
def test_batch_add_node(self):
self.assertEqual(wildfly_batch(self.config0, self.path0, self.attrs0,
wrap=False), self.batch0)
if __name__ == '__main__':
unittest.main()

View File

@ -6,23 +6,30 @@ Ansible action plugin for configuring Wildfly
''' '''
from __future__ import (absolute_import, division, print_function) from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
import re
import json
from ansible.plugins.action import ActionBase from ansible.plugins.action import ActionBase
from ansible.utils.display import Display from ansible.utils.display import Display
from ansible.utils.vars import merge_hash from ansible.utils.vars import merge_hash
display = Display()
import json
import re
from six import string_types from six import string_types
display = Display()
class WildflyError(Exception): class WildflyError(Exception):
pass '''
Basic error class for this module.
'''
class ActionModule(ActionBase): class ActionModule(ActionBase):
'''
The action module.
'''
def run(self, tmp=None, task_vars=None): def run(self, tmp=None, task_vars=None):
super(ActionModule, self).run(tmp, task_vars) super(ActionModule, self).run(tmp, task_vars)
@ -36,7 +43,6 @@ class ActionModule(ActionBase):
cli_command, config, dry_run = read_args(task_args, check_mode) cli_command, config, dry_run = read_args(task_args, check_mode)
# read current config # read current config
# if not self.prev:
cur_result = self.execute_command( cur_result = self.execute_command(
'{} --output-json --command="/:read-resource(recursive)"'.format( '{} --output-json --command="/:read-resource(recursive)"'.format(
cli_command), cli_command),
@ -47,10 +53,10 @@ class ActionModule(ActionBase):
try: try:
cur_state = json.loads(cur_result.get('stdout')).get('result') cur_state = json.loads(cur_result.get('stdout')).get('result')
except Exception as e: except Exception as ex:
return merge_hash(cur_result, dict( return merge_hash(cur_result, dict(
failed=True, failed=True,
msg='Error parsing JSON from Wildfly CLI: {}'.format(str(e)) msg='Error parsing JSON from Wildfly CLI: {}'.format(str(ex))
)) ))
# generate batch script # generate batch script
@ -61,25 +67,27 @@ class ActionModule(ActionBase):
cur_state, cur_state,
item.get('root', '/'), item.get('root', '/'),
item.get('config', {}), item.get('config', {}),
item.get('state', 'present') item.get('state', 'present'),
, False) False)
except Exception as e: except Exception as ex:
return merge_hash(cur_result, dict( return merge_hash(cur_result, dict(
failed=True, failed=True,
msg='Error while generating Wildfly batch: {}'.format(str(e)), msg='Error while generating Wildfly batch: {}'.format(
str(ex)),
stdout=None, stdout=None,
stdout_lines=[], stdout_lines=[],
config_item=item, config_item=item,
batch_script=batch_script batch_script=batch_script
)) ))
result = dict(changed=False,batch_script=batch_script) result = dict(changed=False, batch_script=batch_script,
previous_state=cur_state)
# apply changes # apply changes
if batch_script: if batch_script:
batch_script = 'batch\n{}run-batch\n'.format(batch_script) batch_script = 'batch\n{}run-batch\n'.format(batch_script)
result = dict(changed=True,batch_script=batch_script) result = merge_hash(result, dict(changed=True))
if not dry_run: if not dry_run:
self.execute_command( self.execute_command(
@ -90,7 +98,6 @@ class ActionModule(ActionBase):
return result return result
def execute_command(self, command, stdin=None, check_mode_unsafe=True): def execute_command(self, command, stdin=None, check_mode_unsafe=True):
''' '''
Execute command module and return result Execute command module and return result
@ -107,7 +114,6 @@ class ActionModule(ActionBase):
) )
def read_args(task_args, check_mode): def read_args(task_args, check_mode):
''' '''
Read and validate invocation arguments Read and validate invocation arguments
@ -125,9 +131,9 @@ def read_args(task_args, check_mode):
'Please use "config" argument instead.') 'Please use "config" argument instead.')
# validate config argument # validate config argument
if type(config) is list: if isinstance(config, list):
pass pass
elif type(config) is dict: elif isinstance(config, dict):
config = [dict( config = [dict(
config=config, config=config,
root=task_args.get('root'), root=task_args.get('root'),
@ -142,7 +148,6 @@ def read_args(task_args, check_mode):
return cli_command, config, dry_run return cli_command, config, dry_run
def wildfly_batch(prev, root, attrs, state='present', wrap=True): def wildfly_batch(prev, root, attrs, state='present', wrap=True):
''' '''
Generate Wildfly batch to assert state of configuration item under Generate Wildfly batch to assert state of configuration item under
@ -166,7 +171,7 @@ def wildfly_batch(prev, root, attrs, state='present', wrap=True):
output = '' output = ''
# check current config # check current config
cur = wildfly_config_query(prev, root) cur = config_query(prev, root)
display.v("current config: {}".format(cur)) display.v("current config: {}".format(cur))
@ -174,7 +179,7 @@ def wildfly_batch(prev, root, attrs, state='present', wrap=True):
if cur: if cur:
# node exists, add missing attrs # node exists, add missing attrs
for k in attrs: for k in sorted(attrs):
if k not in cur or cur[k] != attrs[k]: if k not in cur or cur[k] != attrs[k]:
output = '{}{}:write-attribute(name={},value={})\n'.format( output = '{}{}:write-attribute(name={},value={})\n'.format(
output, root, k, format_attr(attrs[k])) output, root, k, format_attr(attrs[k]))
@ -185,21 +190,22 @@ def wildfly_batch(prev, root, attrs, state='present', wrap=True):
# node doesn't exist, add it # node doesn't exist, add it
output = '{}{}:add({})\n'.format( output = '{}{}:add({})\n'.format(
output, root, output, root,
','.join(['{}={}'.format(k,format_attr(attrs[k])) for k in attrs]) ','.join(['{}={}'.format(
k, format_attr(attrs[k])) for k in sorted(attrs)])
) )
wildfly_config_add_node(prev, root, attrs) # config_add_node(prev, root, attrs)
if state.lower() == 'absent' and cur: if state.lower() == 'absent' and cur:
# remove existing node # remove existing node
output = '{}{}:remove()\n'.format(output, root) output = '{}{}:remove()\n'.format(output, root)
if len(output) and wrap: if len(output) > 0 and wrap:
output = 'batch\n{}\nrun-batch\n'.format(output) output = 'batch\n{}\nrun-batch\n'.format(output)
return output return output
def wildfly_config_query(config, path): def config_query(config, path):
''' '''
Given Wildfly configuration as a dict, follow Given Wildfly configuration as a dict, follow
requested path and return corresponding entry. requested path and return corresponding entry.
@ -207,39 +213,18 @@ def wildfly_config_query(config, path):
''' '''
path = WildflyPath(path) path = WildflyPath(path)
ptr = config ptr = config
while len(path): while len(path) > 0:
try: try:
qry = path.lpop() qry = path.lpop()
ptr = ptr[qry] ptr = ptr[qry]
except (KeyError, TypeError): except (KeyError, TypeError):
# either ptr is none (Type), or ptr[qry] not found (Key) # either ptr is none (Type), or ptr[qry] not found (Key)
return None return None
except Exception as e: except Exception as ex:
raise WildflyError( "config query error: {}".format(str(e))) raise WildflyError("config query error: {}".format(str(ex)))
return ptr return ptr
def wildfly_config_add_node(config, path, node):
'''
Given Wildfly configuration as a dict, follow
requested path and add given node.
'''
path = WildflyPath(path)
ptr = config
while len(path):
try:
qry = path.lpop()
ptr = ptr[qry]
except TypeError:
ptr = dict()
except KeyError:
ptr[qry] = dict()
except Exception as e:
raise Exception( "unknown error: {}".format(str(e)))
ptr = merge_hash(ptr,node)
def format_attr(attr): def format_attr(attr):
''' '''
Return valid text representation for attribute as understood by Return valid text representation for attribute as understood by
@ -251,19 +236,27 @@ def format_attr(attr):
if attr is None: if attr is None:
return 'undefined' return 'undefined'
if isinstance(attr, (float)):
return '{:f}'.format(attr)
if isinstance(attr, (int, bool)):
return str(attr)
# list => '[ format(item), ... ]' # list => '[ format(item), ... ]'
if isinstance(attr,list): if isinstance(attr, (list,tuple)):
return '[' + ', '.join( return '[' + ', '.join(
[ '{}'.format( 'undefined' if k_ is None else format_attr(k_)) for k_ in attr ] ['{}'.format(
'undefined' if k_ is None
else format_attr(k_)) for k_ in attr]
) + ']' ) + ']'
# dict => '{ "key" => format(value), ... }' # dict => '{ "key" => format(value), ... }'
if isinstance(attr,dict): if isinstance(attr, dict):
return '{' + ', '.join( return '{' + ', '.join(
[ '"{}" => {}'.format( ['"{}" => {}'.format(
k_, k_, 'undefined' if attr[k_] is None
'undefined' if attr[k_] is None else format_attr(attr[k_]) else format_attr(attr[k_])
) for k_ in list(attr.keys()) ] ) for k_ in list(attr.keys())]
) + '}' ) + '}'
# wrap strings with double quotes # wrap strings with double quotes
@ -283,14 +276,14 @@ class WildflyPath:
def __init__(self, path): def __init__(self, path):
if isinstance(path, string_types): if isinstance(path, string_types):
self.parts = [ self.parts = [
i.replace('\\','').strip() i.replace('\\', '').strip()
for i in re.split(r'((?<!\\)[=/])', path) for i in re.split(r'((?<!\\)[=/])', path)
] ]
elif isinstance(path, WildflyPath): elif isinstance(path, WildflyPath):
self.parts = path.parts self.parts = path.parts
elif isinstance(path, list, tuple): elif isinstance(path, list, tuple):
self.parts = [ self.parts = [
i.replace('\\','').strip() i.replace('\\', '').strip()
for i in path for i in path
] ]
else: else:
@ -298,15 +291,18 @@ class WildflyPath:
def __str__(self): def __str__(self):
return ''.join([ return ''.join([
(i if i in ('','/','=') else i.replace('/','\\/')) (i if i in ('', '/', '=') else i.replace('/', '\\/'))
for i in self.parts for i in self.parts
]) ])
def __len__(self): def __len__(self):
return sum([1 for i in self.parts if i not in ('','=','/')]) return sum([1 for i in self.parts if i not in ('', '=', '/')])
def lpop(self): def lpop(self):
'''
remove first path component and return it
'''
ret = self.parts.pop(0) ret = self.parts.pop(0)
while ret in ('','/','='): while ret in ('', '/', '='):
ret = self.parts.pop(0) ret = self.parts.pop(0)
return ret return ret