Add PostgreSQL Language management functionality

Fixes #29874
This commit is contained in:
Andrew Colin Kissa 2015-12-20 09:37:41 +02:00
parent 037bcdea27
commit a826720371
4 changed files with 621 additions and 0 deletions

View File

@ -1993,3 +1993,206 @@ def schema_list(dbname,
ret[row['name']] = retrow
return ret
def language_list(
maintenance_db,
user=None,
host=None,
port=None,
password=None,
runas=None):
'''
Return a list of languages in a database.
CLI Example:
.. code-block:: bash
salt '*' postgres.language_list dbname
maintenance_db
The database to check
user
database username if different from config or default
password
user password if any password for a specified user
host
Database host if different from config or default
port
Database port if different from config or default
runas
System user all operations should be performed on behalf of
'''
ret = {}
query = 'SELECT lanname AS "Name" FROM pg_language'
rows = psql_query(
query,
runas=runas,
host=host,
user=user,
port=port,
maintenance_db=maintenance_db,
password=password)
for row in rows:
ret[row['Name']] = row['Name']
return ret
def language_exists(
name,
maintenance_db,
user=None,
host=None,
port=None,
password=None,
runas=None):
'''
Checks if language exists in a database.
CLI Example:
.. code-block:: bash
salt '*' postgres.language_exists plpgsql dbname
name
Language to check for
maintenance_db
The database to check in
user
database username if different from config or default
password
user password if any password for a specified user
host
Database host if different from config or default
port
Database port if different from config or default
runas
System user all operations should be performed on behalf of
'''
languages = language_list(
maintenance_db, user=user, host=host,
port=port, password=password,
runas=runas)
return name in languages
def language_create(name,
maintenance_db,
user=None,
host=None,
port=None,
password=None,
runas=None):
'''
Installs a language into a database
CLI Example:
.. code-block:: bash
salt '*' postgres.language_create plpgsql dbname
name
Language to install
maintenance_db
The database to install the language in
user
database username if different from config or default
password
user password if any password for a specified user
host
Database host if different from config or default
port
Database port if different from config or default
runas
System user all operations should be performed on behalf of
'''
if language_exists(name, maintenance_db):
log.info('Language %s already exists in %s', name, maintenance_db)
return False
query = 'CREATE LANGUAGE {0}'.format(name)
ret = _psql_prepare_and_run(['-c', query],
user=user,
host=host,
port=port,
maintenance_db=maintenance_db,
password=password,
runas=runas)
return ret['retcode'] == 0
def language_remove(name,
maintenance_db,
user=None,
host=None,
port=None,
password=None,
runas=None):
'''
Removes a language from a database
CLI Example:
.. code-block:: bash
salt '*' postgres.language_remove plpgsql dbname
name
Language to remove
maintenance_db
The database to install the language in
user
database username if different from config or default
password
user password if any password for a specified user
host
Database host if different from config or default
port
Database port if different from config or default
runas
System user all operations should be performed on behalf of
'''
if not language_exists(name, maintenance_db):
log.info('Language %s does not exist in %s', name, maintenance_db)
return False
query = 'DROP LANGUAGE {0}'.format(name)
ret = _psql_prepare_and_run(['-c', query],
user=user,
host=host,
port=port,
runas=runas,
maintenance_db=maintenance_db,
password=password)
return ret['retcode'] == 0

View File

@ -0,0 +1,162 @@
# -*- coding: utf-8 -*-
'''
Management of PostgreSQL languages
==================================
The postgres_language module is used to create and manage Postgres languages.
Languages can be set as either absent or present
.. code-block:: yaml
plpgsql:
postgres_language.present:
- maintenance_db: testdb
.. code-block:: yaml
plpgsql:
postgres_language.absent:
- maintenance_db: testdb
.. versionadded:: Boron
'''
from __future__ import absolute_import
def __virtual__():
'''
Only load if the postgres module is present
'''
return 'postgres.language_create' in __salt__
def present(name,
maintenance_db,
user=None,
db_password=None,
db_host=None,
db_port=None,
db_user=None):
'''
Ensure that a named language is present in the specified
database.
name
The name of the language to install
maintenance_db
The name of the database in which the language is to be installed
user
System user all operations should be performed on behalf of
db_user
database username if different from config or default
db_password
user password if any password for a specified user
db_host
Database host if different from config or default
db_port
Database port if different from config or default
'''
ret = {
'name': name,
'changes': {},
'result': True,
'comment': 'Language {0} is already installed'.format(name)
}
dbargs = {
'runas': user,
'host': db_host,
'user': db_user,
'port': db_port,
'password': db_password,
}
languages = __salt__['postgres.language_list'](maintenance_db, **dbargs)
if name not in languages:
if __opts__['test']:
ret['result'] = None
ret['comment'] = 'Language {0} is set to be installed'.format(
name)
return ret
if __salt__['postgres.language_create'](name, maintenance_db,
**dbargs):
ret['comment'] = 'Language {0} has been installed'.format(name)
ret['changes'][name] = 'Present'
else:
ret['comment'] = 'Failed to install language {0}'.format(name)
ret['result'] = False
return ret
def absent(
name,
maintenance_db,
user=None,
db_password=None,
db_host=None,
db_port=None,
db_user=None):
'''
Ensure that a named language is absent in the specified
database.
name
The name of the language to remove
maintenance_db
The name of the database in which the language is to be installed
user
System user all operations should be performed on behalf of
db_user
database username if different from config or default
db_password
user password if any password for a specified user
db_host
Database host if different from config or default
db_port
Database port if different from config or default
'''
ret = {
'name': name,
'changes': {},
'result': True,
'comment': ''
}
dbargs = {
'runas': user,
'host': db_host,
'user': db_user,
'port': db_port,
'password': db_password,
}
if __salt__['postgres.language_exists'](name, maintenance_db, **dbargs):
if __opts__['test']:
ret['result'] = None
ret['comment'] = 'Language {0} is set to be removed'.format(name)
return ret
if __salt__['postgres.language_remove'](name, **dbargs):
ret['comment'] = 'Language {0} has been removed'.format(name)
ret['changes'][name] = 'Absent'
return ret
else:
ret['comment'] = 'Failed to remove language {0}'.format(name)
ret['result'] = False
ret['comment'] = 'Language {0} is not present ' \
'so it cannot be removed'.format(name)
return ret

View File

@ -33,6 +33,14 @@ test_list_schema_csv = (
'pg_toast,postgres,""'
)
test_list_language_csv = (
'Name\n'
'internal\n'
'c\n'
'sql\n'
'plpgsql\n'
)
if NO_MOCK is False:
SALT_STUB = {
@ -862,6 +870,125 @@ class PostgresTestCase(TestCase):
)
self.assertFalse(ret)
@patch('salt.modules.postgres._run_psql',
Mock(return_value={'retcode': 0,
'stdout': test_list_language_csv}))
def test_language_list(self):
'''
Test language listing
'''
ret = postgres.language_list(
'testdb',
user='testuser',
host='testhost',
port='testport',
password='foo'
)
self.assertDictEqual(ret,
{'c': 'c',
'internal': 'internal',
'plpgsql': 'plpgsql',
'sql': 'sql'})
@patch('salt.modules.postgres._run_psql',
Mock(return_value={'retcode': 0}))
@patch('salt.modules.postgres.psql_query',
Mock(return_value=[
{'Name': 'internal'},
{'Name': 'c'},
{'Name': 'sql'},
{'Name': 'plpgsql'}]))
@patch('salt.modules.postgres.language_exists', Mock(return_value=True))
def test_language_exists(self):
'''
Test language existance check
'''
ret = postgres.language_exists(
'sql',
'testdb'
)
self.assertTrue(ret)
@patch('salt.modules.postgres._run_psql',
Mock(return_value={'retcode': 0}))
@patch('salt.modules.postgres.language_exists', Mock(return_value=False))
def test_language_create(self):
'''
Test language creation - does not exist in db
'''
postgres.language_create(
'plpythonu',
'testdb',
runas='user',
host='testhost',
port='testport',
user='testuser',
password='testpassword'
)
postgres._run_psql.assert_called_once_with(
['/usr/bin/pgsql', '--no-align', '--no-readline',
'--no-password', '--username', 'testuser', '--host',
'testhost', '--port', 'testport', '--dbname', 'testdb',
'-c', 'CREATE LANGUAGE plpythonu'],
host='testhost', port='testport',
password='testpassword', user='testuser', runas='user')
@patch('salt.modules.postgres.language_exists', Mock(return_value=True))
def test_language_create_exists(self):
'''
Test language creation - already exists in db
'''
ret = postgres.language_create(
'plpythonu',
'testdb',
runas='user',
host='testhost',
port='testport',
user='testuser',
password='testpassword'
)
self.assertFalse(ret)
@patch('salt.modules.postgres._run_psql',
Mock(return_value={'retcode': 0}))
@patch('salt.modules.postgres.language_exists', Mock(return_value=True))
def test_language_remove(self):
'''
Test language removal - exists in db
'''
postgres.language_remove(
'plpgsql',
'testdb',
runas='user',
host='testhost',
port='testport',
user='testuser',
password='testpassword'
)
postgres._run_psql.assert_called_once_with(
['/usr/bin/pgsql', '--no-align', '--no-readline',
'--no-password', '--username', 'testuser', '--host',
'testhost', '--port', 'testport', '--dbname', 'testdb',
'-c', 'DROP LANGUAGE plpgsql'],
host='testhost', port='testport',
password='testpassword', user='testuser', runas='user')
@patch('salt.modules.postgres.language_exists', Mock(return_value=False))
def test_language_remove_non_exist(self):
'''
Test language removal - does not exist in db
'''
ret = postgres.language_remove(
'plpgsql',
'testdb',
runas='user',
host='testhost',
port='testport',
user='testuser',
password='testpassword'
)
self.assertFalse(ret)
if __name__ == '__main__':
from integration import run_tests
run_tests(PostgresTestCase, needs_daemon=False)

View File

@ -0,0 +1,129 @@
# -*- coding: utf-8 -*-
'''
:codeauthor: :email:`Andrew Colin Kissa <andrew@topdog.za.net>`
'''
from __future__ import absolute_import
from salttesting import skipIf, TestCase
from salttesting.mock import (
NO_MOCK,
NO_MOCK_REASON,
MagicMock,
patch
)
from salttesting.helpers import ensure_in_syspath
ensure_in_syspath('../../')
from salt.states import postgres_language
@skipIf(NO_MOCK, NO_MOCK_REASON)
class PostgresLanguageTestCase(TestCase):
'''
Test cases for salt.states.postgres_language
'''
def setUp(self):
'''
Setup data for the tests
'''
postgres_language.__opts__ = {}
postgres_language.__salt__ = {}
self.name = 'plpgsql'
self.ret = {'name': self.name,
'changes': {},
'result': False,
'comment': ''}
self.mock_true = MagicMock(return_value=True)
self.mock_false = MagicMock(return_value=False)
self.mock_empty_language_list = MagicMock(return_value={})
self.mock_language_list = MagicMock(
return_value={'plpgsql': self.name})
def test_present_existing(self):
'''
Test present, language is already present in database
'''
with patch.dict(postgres_language.__salt__,
{'postgres.language_list': self.mock_language_list}):
comt = 'Language {0} is already installed'.format(self.name)
self.ret.update({'comment': comt, 'result': True})
self.assertDictEqual(
postgres_language.present(self.name, 'testdb'), self.ret)
def test_present_non_existing_pass(self):
'''
Test present, language not present in database - pass
'''
with patch.dict(postgres_language.__salt__,
{'postgres.language_list': self.mock_empty_language_list,
'postgres.language_create': self.mock_true}):
with patch.dict(postgres_language.__opts__, {'test': True}):
comt = 'Language {0} is set to be installed'.format(self.name)
self.ret.update({'comment': comt, 'result': None})
self.assertDictEqual(
postgres_language.present(self.name, 'testdb'), self.ret)
with patch.dict(postgres_language.__opts__, {'test': False}):
comt = 'Language {0} has been installed'.format(self.name)
self.ret.update({'comment': comt,
'result': True,
'changes': {'plpgsql': 'Present'}})
self.assertDictEqual(
postgres_language.present(self.name, 'testdb'), self.ret)
def test_present_non_existing_fail(self):
'''
Test present, language not present in database - fail
'''
with patch.dict(postgres_language.__salt__,
{'postgres.language_list': self.mock_empty_language_list,
'postgres.language_create': self.mock_false}):
with patch.dict(postgres_language.__opts__, {'test': True}):
comt = 'Language {0} is set to be installed'.format(self.name)
self.ret.update({'comment': comt, 'result': None})
self.assertDictEqual(
postgres_language.present(self.name, 'testdb'), self.ret)
with patch.dict(postgres_language.__opts__, {'test': False}):
comt = 'Failed to install language {0}'.format(self.name)
self.ret.update({'comment': comt, 'result': False})
self.assertDictEqual(
postgres_language.present(self.name, 'testdb'), self.ret)
def test_absent_existing(self):
'''
Test absent, language present in database
'''
with patch.dict(postgres_language.__salt__,
{'postgres.language_exists': self.mock_true,
'postgres.language_remove': self.mock_true}):
with patch.dict(postgres_language.__opts__, {'test': True}):
comt = 'Language {0} is set to be removed'.format(self.name)
self.ret.update({'comment': comt, 'result': None})
self.assertDictEqual(
postgres_language.absent(self.name, 'testdb'), self.ret)
with patch.dict(postgres_language.__opts__, {'test': False}):
comt = 'Language {0} has been removed'.format(self.name)
self.ret.update({'comment': comt,
'result': True,
'changes': {'plpgsql': 'Absent'}})
self.assertDictEqual(
postgres_language.absent(self.name, 'testdb'), self.ret)
def test_absent_non_existing(self):
'''
Test absent, language not present in database
'''
with patch.dict(postgres_language.__salt__,
{'postgres.language_exists': self.mock_false}):
with patch.dict(postgres_language.__opts__, {'test': True}):
comt = 'Language {0} is not present so ' \
'it cannot be removed'.format(self.name)
self.ret.update({'comment': comt, 'result': True})
self.assertDictEqual(
postgres_language.absent(self.name, 'testdb'), self.ret)