123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151 |
- #! /usr/bin/env python3
-
- import argparse
- import sys
- import os
- import os.path
- import re
- import distutils.version
- import subprocess
- import datetime
-
-
- def file_runner_pgsql(folder, file):
- subprocess.call(['psql', '-f', os.path.join(folder, file)])
-
- def file_runner_pgsql_gz(folder, file):
- p1 = subprocess.Popen(['gunzip', '-c', os.path.join(folder, file)], stdout=subprocess.PIPE)
- p2 = subprocess.Popen(["psql"], stdin=p1.stdout)
- p1.stdout.close()
- p2.communicate()
-
- def file_runner_exec(folder, file):
- subprocess.call([os.path.join(folder, file)])
-
-
- def list_files(folder):
- files = [f for f in os.listdir(folder) if os.path.isfile(os.path.join(folder, f))]
- files.sort()
- return files
-
- def sort_versions(versions):
- versions.sort(key=distutils.version.StrictVersion)
-
- def list_versions(files):
- versions = []
- for file in files:
- version = file.split('_')[0]
- if re.match('^[0-9]+\.[0-9]+\.[0-9]+$', version):
- if version not in versions:
- versions.append(version)
- sort_versions(versions)
- return versions
-
- def run_file(folder, file, no_run, file_runners):
- can_run = False
- for file_runner_ext in file_runners:
- if file.endswith(file_runner_ext):
- print('Running file %s' % (file))
- can_run = True
- if not no_run:
- try:
- sys.stdout.flush()
- file_runners[file_runner_ext](folder, file)
- except Exception as e:
- print('Failed to run file: %s' % (e))
- if not can_run:
- print('Ignoring file %s' % (file))
-
- def run_migration(folder, files, version, no_run, file_runners):
- for file in files:
- if file.startswith('%s_' % (version)):
- run_file(folder, file, no_run, file_runners)
-
- def run_migrations(folder, version_from, version_to, no_run, file_runners, log_file):
- files = list_files(folder)
- versions = list_versions(files)
-
- if not versions:
- print('No migration available. Exiting.')
- return 0
-
- if version_to is None:
- version_to = versions[-1]
- print('Defaulting VERSION_TO to %s' % (version_to))
- if version_to not in versions:
- print('Could not find VERSION_TO %s' % (version_to))
- return 1
- if version_from is not None and version_from not in versions:
- print('Could not find VERSION_FROM %s' % (version_from))
- return 1
-
- if no_run:
- print('Not running because --no-run was specified')
-
- if version_from is None:
- msg = 'Initialising from %s (inclusive) to %s (inclusive)' % (versions[0], version_to)
- else:
- msg = 'Migrating from %s (exclusive) to %s (inclusive)' % (version_from, version_to)
-
- print(msg)
-
- if not no_run:
- try:
- with open(log_file, "a") as f:
- f.write('%s: %s in %s\n' % (datetime.datetime.now(), msg, folder))
- except Exception as e:
- print('Failed to write logs in %: %s' % (log_file, e))
-
- version_from_obj = distutils.version.StrictVersion('0.0.0' if version_from is None else version_from)
- version_to_obj = distutils.version.StrictVersion(version_to)
-
- last_version = version_from
- for version in versions:
- version_obj = distutils.version.StrictVersion(version)
- if version_from_obj < version_obj and version_obj <= version_to_obj:
- if last_version is None:
- print('Initialising %s' % (version))
- else:
- print('Migrating from %s to %s' % (last_version, version))
- run_migration(folder, files, version, no_run, file_runners)
- last_version = version
-
- return 0
-
- def main():
- parser = argparse.ArgumentParser(description='Migrate container configuration/data')
- parser.add_argument('--folder', dest='folder', default='/docker-entrypoint-initdb.d/', help='Folder to use for migration')
- parser.add_argument('--init', dest='is_init', default=False, action='store_true', help='Run all migrations')
- parser.add_argument('--migrate', dest='is_migrate', default=False, action='store_true', help='Run all migrations between VERSION_FROM (exclusive) and VERSION_TO (inclusive)')
- parser.add_argument('--no-run', dest='no_run', default=False, action='store_true', help='Do not run migration, just print')
- parser.add_argument('--version-from', dest='version_from', default=None, help='Current version, required if using --migrate, can not be used --init')
- parser.add_argument('--version-to', dest='version_to', default=None, help='Final version, default to last available version is not given')
- parser.add_argument('--log-file', dest='log_file', default='/var/lib/postgresql/data/migrate.py.log', help='File to log migrations')
-
- args = parser.parse_args()
-
- file_runners = {
- 'sql': file_runner_pgsql,
- 'sql.gz': file_runner_pgsql_gz,
- 'sh': file_runner_exec
- }
-
- if args.is_init and args.is_migrate:
- print('--init and --migrate can not be used together')
- return 64
- if args.is_init:
- if args.version_from is not None:
- print('--version-from can not be used with --init')
- return 64
- return run_migrations(args.folder, None, args.version_to, args.no_run, file_runners, args.log_file)
- elif args.is_migrate:
- if args.version_from is None:
- print('--version-from is required. Use --init to run all migrations')
- return 64
- return run_migrations(args.folder, args.version_from, args.version_to, args.no_run, file_runners, args.log_file)
- else:
- print('Missing --init or --migrate')
- return 64
-
- if __name__ == '__main__':
- sys.exit(main())
|