Skip to content

Instantly share code, notes, and snippets.

@OrangeDog
Last active September 6, 2023 08:00
Show Gist options
  • Save OrangeDog/49871e06b017318b7498b810e2ad7caa to your computer and use it in GitHub Desktop.
Save OrangeDog/49871e06b017318b7498b810e2ad7caa to your computer and use it in GitHub Desktop.
Stateful DDL
# Need to bypass module loader in order to access private functions
from salt.modules import postgres
def __init__(opts):
postgres.__grains__ = __grains__
postgres.__opts__ = opts
postgres.__salt__ = __salt__
def _kwargs(kwargs):
"""
Used to forward common arguments for postgres module
"""
return {k: v for k, v in kwargs.items() if k in (
'user', 'host', 'port', 'maintenance_db', 'password', 'runas')}
def apply_script(dbname, source, source_hash, transaction=True, **kwargs):
owner = kwargs.get('runas', 'postgres')
tmp_filename = __utils__['files.mkstemp']()
comment = "COMMENT ON DATABASE %s IS '%s'" % (dbname, source_hash)
args = ['--set', 'ON_ERROR_STOP=1',
'--echo-all',
'--file', tmp_filename,
'--command', comment]
if transaction:
args.append('--single-transaction')
try:
cp_kwargs = {k: v for k, v in kwargs.items()
if k in ('template', 'saltenv')}
cp_ret = __salt__['cp.get_template'](source, tmp_filename, **cp_kwargs)
if cp_ret != tmp_filename:
return False
__salt__['file.chown'](tmp_filename, owner, owner)
psql_ret = postgres._psql_prepare_and_run(args, maintenance_db=dbname,
**_kwargs(kwargs))
return psql_ret['retcode'] == 0
finally:
__utils__['files.remove'](tmp_filename)
def get_database_comment(name, **kwargs):
sql = "SELECT description FROM pg_shdescription " \
"JOIN pg_database ON objoid = pg_database.oid " \
"WHERE datname = '%s'" % name
rows = __salt__['postgres.psql_query'](sql, **_kwargs(kwargs))
return rows[0]['description'] if rows else None
def apply(name, source, source_hash=None, transaction=True, **kwargs):
"""
Manage versioning of DDL using a script and a database comment.
The DDL script must be applicable to any previous state, i.e. it must
use ``CREATE IF EXISTS`` and ``ALTER`` statements.
External changes to database schema will not trigger this to run.
.. warning::
The hash is of the source file, so any templating must be stateless.
:param name: Name of database to which to apply the script
:param source: Source of the script (see ``file.present``)
:param source_hash: Override hash used for versioning
:param transaction: Whether the script should be run within a transaction
"""
ret = {'name': name, 'changes': {}, 'comment': ''}
if source_hash is None:
source_hash = __salt__['cp.hash_file'](source, saltenv=__env__)['hsum']
comment = __salt__['pg_ddl.get_database_comment'](name, **kwargs)
if source_hash == comment:
ret['comment'] = 'Script is already applied to %s' % name
ret['result'] = True
return ret
ret['changes'].update({'old': comment, 'new': source_hash})
if __opts__['test']:
ret['comment'] = 'Script would be applied to %s' % name
ret['result'] = None
return ret
ret['result'] = __salt__['pg_ddl.apply_script'](name, source, source_hash,
transaction=transaction,
saltenv=__env__,
**kwargs)
if ret['result']:
ret['comment'] = 'Script applied to %s' % name
else:
ret['comment'] = 'Failed to apply script to %s' % name
return ret
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment