2019-01-06 14:14:47 -06:00
|
|
|
# -*- coding: utf-8; -*-
|
|
|
|
################################################################################
|
|
|
|
#
|
|
|
|
# Rattail -- Retail Software Framework
|
2023-02-23 13:05:54 -06:00
|
|
|
# Copyright © 2010-2023 Lance Edgar
|
2019-01-06 14:14:47 -06:00
|
|
|
#
|
|
|
|
# This file is part of Rattail.
|
|
|
|
#
|
|
|
|
# Rattail is free software: you can redistribute it and/or modify it under the
|
|
|
|
# terms of the GNU General Public License as published by the Free Software
|
|
|
|
# Foundation, either version 3 of the License, or (at your option) any later
|
|
|
|
# version.
|
|
|
|
#
|
|
|
|
# Rattail is distributed in the hope that it will be useful, but WITHOUT ANY
|
|
|
|
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
|
|
|
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
|
|
|
# details.
|
|
|
|
#
|
|
|
|
# You should have received a copy of the GNU General Public License along with
|
|
|
|
# Rattail. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
#
|
|
|
|
################################################################################
|
|
|
|
"""
|
|
|
|
Fabric library for Backup app
|
|
|
|
"""
|
|
|
|
|
|
|
|
import os
|
|
|
|
import datetime
|
|
|
|
|
2019-09-26 23:40:55 -05:00
|
|
|
from rattail_fabric2 import apt, borg, python, exists, make_deploy, mkdir, UNSPECIFIED
|
2019-01-06 14:14:47 -06:00
|
|
|
|
|
|
|
|
|
|
|
deploy_generic = make_deploy(__file__)
|
|
|
|
|
|
|
|
|
2019-09-11 18:36:43 -05:00
|
|
|
def deploy_rattail_backup_script(c, **context):
|
|
|
|
"""
|
|
|
|
Deploy the generic `rattail-backup` script
|
|
|
|
"""
|
|
|
|
context.setdefault('envname', 'backup')
|
|
|
|
deploy_generic(c, 'backup/rattail-backup.mako', '/usr/local/bin/rattail-backup',
|
|
|
|
mode='0700', context=context, use_sudo=True)
|
|
|
|
|
|
|
|
|
2019-01-06 14:14:47 -06:00
|
|
|
def deploy_backup_everything(c, **context):
|
|
|
|
"""
|
|
|
|
Deploy the generic `backup-everything` script
|
|
|
|
"""
|
|
|
|
context.setdefault('envname', 'backup')
|
|
|
|
context.setdefault('user', 'rattail')
|
|
|
|
deploy_generic(c, 'backup/backup-everything.mako', '/usr/local/bin/backup-everything',
|
|
|
|
mode='0700', context=context, use_sudo=True)
|
|
|
|
|
|
|
|
|
|
|
|
def deploy_backup_app(c, deploy, envname, mkvirtualenv=True, user='rattail',
|
2020-09-29 17:59:31 -05:00
|
|
|
python_exe='/usr/bin/python3',
|
2019-09-13 13:23:09 -05:00
|
|
|
install_borg=False,
|
2022-11-27 18:47:43 -06:00
|
|
|
pyfuse3=False,
|
2019-09-16 14:52:36 -05:00
|
|
|
link_borg_to_bin=True,
|
2019-09-26 23:40:55 -05:00
|
|
|
install_luigi=False,
|
|
|
|
luigi_history_db=None,
|
|
|
|
luigi_listen_address='0.0.0.0',
|
2019-09-13 13:23:09 -05:00
|
|
|
install_rattail=True,
|
|
|
|
config=None,
|
2019-09-11 18:36:43 -05:00
|
|
|
rattail_backup_script=None,
|
2019-09-13 13:23:09 -05:00
|
|
|
everything=None,
|
|
|
|
crontab=None,
|
2023-02-23 13:05:54 -06:00
|
|
|
crontab_mailto=None,
|
2019-09-13 13:23:09 -05:00
|
|
|
runat=UNSPECIFIED,
|
2023-02-17 11:01:29 -06:00
|
|
|
logrotate=False,
|
2019-09-13 13:23:09 -05:00
|
|
|
context={}):
|
2019-01-06 14:14:47 -06:00
|
|
|
"""
|
|
|
|
Make an app which can run backups for the server.
|
|
|
|
"""
|
2019-09-13 13:23:09 -05:00
|
|
|
if install_rattail and not config:
|
2019-09-11 17:33:40 -05:00
|
|
|
path = '{}/rattail.conf.mako'.format(envname)
|
2019-01-06 14:14:47 -06:00
|
|
|
if deploy.local_exists(path):
|
|
|
|
config = path
|
|
|
|
else:
|
2019-09-11 17:33:40 -05:00
|
|
|
path = '{}/rattail.conf'.format(envname)
|
|
|
|
if deploy.local_exists(path):
|
|
|
|
config = path
|
|
|
|
else:
|
2022-02-16 15:26:46 -06:00
|
|
|
raise ValueError("Config file not found for backup; "
|
|
|
|
"please add {} to your deploy folder".format(path))
|
2019-01-06 14:14:47 -06:00
|
|
|
|
2019-09-13 13:23:09 -05:00
|
|
|
if install_borg:
|
|
|
|
borg.install_dependencies(c)
|
2019-01-06 14:14:47 -06:00
|
|
|
|
2019-09-26 23:40:55 -05:00
|
|
|
if install_luigi:
|
|
|
|
c.sudo('supervisorctl stop backup:')
|
|
|
|
|
2019-01-06 14:14:47 -06:00
|
|
|
# virtualenv
|
|
|
|
envpath = '/srv/envs/{}'.format(envname)
|
2020-09-29 17:59:31 -05:00
|
|
|
if mkvirtualenv and not exists(c, envpath):
|
2023-01-02 15:01:39 -06:00
|
|
|
mkdir(c, envpath, use_sudo=True, owner=user)
|
2020-09-29 17:59:31 -05:00
|
|
|
python.mkvirtualenv(c, envname, python=python_exe, runas_user=user)
|
2022-11-27 18:47:43 -06:00
|
|
|
c.sudo("bash -c 'PIP_CONFIG_FILE={0}/pip.conf {0}/bin/pip install -U pip setuptools wheel'".format(envpath),
|
|
|
|
user=user)
|
2019-01-06 14:14:47 -06:00
|
|
|
|
2019-09-13 13:23:09 -05:00
|
|
|
if install_rattail:
|
|
|
|
|
|
|
|
# rattail
|
2023-01-02 15:01:39 -06:00
|
|
|
mkdir(c, os.path.join(envpath, 'src'), use_sudo=True, runas_user=user)
|
2019-09-13 13:23:09 -05:00
|
|
|
if not exists(c, os.path.join(envpath, 'src/rattail')):
|
2021-11-10 20:56:55 -06:00
|
|
|
c.sudo('git clone https://kallithea.rattailproject.org/rattail-project/rattail {}/src/rattail'.format(envpath), user=user)
|
2022-11-28 16:49:56 -06:00
|
|
|
c.sudo("bash -c 'PIP_CONFIG_FILE={0}/pip.conf {0}/bin/pip install --editable {0}/src/rattail'".format(envpath),
|
|
|
|
user=user)
|
2019-09-13 13:23:09 -05:00
|
|
|
deploy_generic(c, 'backup/git-exclude', os.path.join(envpath, 'src/rattail/.git/info/exclude'), use_sudo=True, owner=user)
|
|
|
|
|
|
|
|
# config
|
2021-05-04 15:49:46 -05:00
|
|
|
if not exists(c, os.path.join(envpath, 'app')):
|
|
|
|
c.sudo("bash -c 'cd {} && bin/rattail make-appdir'".format(envpath),
|
|
|
|
user=user)
|
2019-09-22 20:15:15 -05:00
|
|
|
# note, config is owned by root regardless of `user` - since we always run backups as root
|
2021-05-04 15:49:46 -05:00
|
|
|
deploy(c, config, os.path.join(envpath, 'app/rattail.conf'),
|
|
|
|
use_sudo=True, owner='root:{}'.format(user), mode='0640',
|
|
|
|
context=context)
|
|
|
|
if not exists(c, os.path.join(envpath, 'app', 'quiet.conf')):
|
|
|
|
c.sudo("bash -c 'cd {} && bin/rattail -c app/rattail.conf make-config -T quiet -O app/'".format(envpath),
|
|
|
|
user=user)
|
|
|
|
if not exists(c, os.path.join(envpath, 'app', 'silent.conf')):
|
2021-11-10 20:56:55 -06:00
|
|
|
c.sudo("bash -c 'cd {} && bin/rattail -c app/rattail.conf make-config -T silent -O app/'".format(envpath),
|
2021-05-04 15:49:46 -05:00
|
|
|
user=user)
|
2019-09-13 13:23:09 -05:00
|
|
|
|
|
|
|
# rattail-backup script
|
|
|
|
script_context = dict(context)
|
|
|
|
script_context['envname'] = envname
|
|
|
|
if rattail_backup_script:
|
|
|
|
deploy(c, rattail_backup_script, '/usr/local/bin/rattail-backup', mode='0700', use_sudo=True,
|
|
|
|
context=script_context)
|
|
|
|
else:
|
|
|
|
deploy_rattail_backup_script(c, **script_context)
|
|
|
|
|
|
|
|
# borg
|
|
|
|
if install_borg:
|
2022-11-27 18:47:43 -06:00
|
|
|
if isinstance(install_borg, list):
|
|
|
|
packages = install_borg
|
|
|
|
elif isinstance(install_borg, str):
|
|
|
|
packages = [install_borg]
|
2019-09-13 13:23:09 -05:00
|
|
|
else:
|
2022-11-27 18:47:43 -06:00
|
|
|
packages = ['msgpack']
|
|
|
|
if pyfuse3:
|
|
|
|
apt.install(c, 'libfuse3-dev')
|
|
|
|
packages.append('borgbackup[pyfuse3]')
|
|
|
|
else:
|
|
|
|
# TODO: this is legacy and should stop being default
|
|
|
|
packages.append('borgbackup[fuse]')
|
|
|
|
c.sudo("bash -c 'PIP_CONFIG_FILE={0}/pip.conf {0}/bin/pip install {1}'".format(envpath, ' '.join(packages)),
|
2021-01-13 13:13:35 -06:00
|
|
|
user=user)
|
2019-09-16 14:52:36 -05:00
|
|
|
if link_borg_to_bin:
|
|
|
|
c.sudo("ln -sf {}/bin/borg /usr/local/bin/borg".format(envpath))
|
2019-09-11 18:36:43 -05:00
|
|
|
|
2019-09-26 23:40:55 -05:00
|
|
|
# luigi
|
|
|
|
if install_luigi:
|
|
|
|
packages = ['luigi']
|
|
|
|
if luigi_history_db:
|
|
|
|
packages.append('SQLAlchemy')
|
|
|
|
if luigi_history_db.startswith('postgresql://'):
|
|
|
|
packages.append('psycopg2')
|
2022-11-28 16:49:56 -06:00
|
|
|
c.sudo("bash -c 'PIP_CONFIG_FILE={0}/pip.conf {0}/bin/pip install {1}'".format(envpath, ' '.join(packages)),
|
|
|
|
user=user)
|
2019-09-26 23:40:55 -05:00
|
|
|
|
|
|
|
# basic config
|
|
|
|
mkdir(c, ['{}/app/luigitasks'.format(envpath),
|
|
|
|
'{}/app/luigi'.format(envpath),
|
|
|
|
'{}/app/luigi/log'.format(envpath),
|
|
|
|
'{}/app/work'.format(envpath),
|
|
|
|
], owner=user, use_sudo=True)
|
|
|
|
c.sudo('touch {}/app/luigitasks/__init__.py'.format(envpath), user=user)
|
|
|
|
deploy_generic(c, 'backup/luigi.cfg.mako', '{}/app/luigi/luigi.cfg'.format(envpath),
|
|
|
|
owner=user, mode='0600', use_sudo=True,
|
|
|
|
context={'envpath': envpath, 'history_db': luigi_history_db})
|
|
|
|
deploy_generic(c, 'backup/luigi-logging.conf.mako', '{}/app/luigi/luigi-logging.conf'.format(envpath),
|
|
|
|
owner=user, use_sudo=True, context={'envpath': envpath})
|
|
|
|
|
2019-09-30 15:37:21 -05:00
|
|
|
# needed for restarting luigi tasks
|
|
|
|
apt.install(c, 'at')
|
|
|
|
|
2019-09-26 23:40:55 -05:00
|
|
|
# app/luigitasks/overnight.py - should define the OvernightBackups wrapper task
|
|
|
|
path = '{}/luigi-overnight.py'.format(envname)
|
|
|
|
if deploy.local_exists(path):
|
|
|
|
deploy(c, path, '{}/app/luigitasks/overnight.py'.format(envpath),
|
|
|
|
owner=user, use_sudo=True)
|
|
|
|
else:
|
|
|
|
deploy_generic(c, 'backup/luigi-overnight.py', '{}/app/luigitasks/overnight.py'.format(envpath),
|
|
|
|
owner=user, use_sudo=True)
|
|
|
|
|
|
|
|
# app/overnight-backups.sh - generic script to invoke OvernightBackups task
|
|
|
|
deploy_generic(c, 'backup/overnight-backups.sh.mako', '{}/app/overnight-backups.sh'.format(envpath),
|
|
|
|
owner=user, mode='0755', use_sudo=True, context={'envpath': envpath})
|
|
|
|
|
2019-09-30 15:37:21 -05:00
|
|
|
# app/restart-overnight-backups.sh - generic script to restart OvernightBackups task
|
|
|
|
deploy_generic(c, 'backup/restart-overnight-backups.sh.mako', '{}/app/restart-overnight-backups.sh'.format(envpath),
|
|
|
|
owner=user, mode='0755', use_sudo=True, context={'envpath': envpath})
|
|
|
|
|
2019-09-26 23:40:55 -05:00
|
|
|
# supervisor / luigid
|
|
|
|
apt.install(c, 'supervisor')
|
|
|
|
deploy_generic(c, 'backup/supervisor.conf.mako', '/etc/supervisor/conf.d/backup.conf',
|
|
|
|
use_sudo=True, context={
|
|
|
|
'envpath': envpath, 'user': user,
|
|
|
|
'listen_address': luigi_listen_address})
|
|
|
|
c.sudo('supervisorctl update')
|
|
|
|
c.sudo('supervisorctl start backup:')
|
|
|
|
|
2019-09-30 15:37:21 -05:00
|
|
|
# upgrade script
|
|
|
|
if install_rattail:
|
|
|
|
deploy_generic(c, 'backup/upgrade.sh.mako', '{}/app/upgrade.sh'.format(envpath),
|
|
|
|
owner=user, mode='0755', use_sudo=True, context={'envpath': envpath, 'user': user})
|
|
|
|
|
2019-01-06 14:14:47 -06:00
|
|
|
# backup-everything script
|
2019-09-13 13:23:09 -05:00
|
|
|
if install_rattail or everything:
|
|
|
|
everything_context = dict(context)
|
|
|
|
everything_context['envname'] = envname
|
|
|
|
everything_context['user'] = user
|
|
|
|
if everything:
|
|
|
|
deploy(c, everything, '/usr/local/bin/backup-everything', mode='0700', context=everything_context, use_sudo=True)
|
|
|
|
else:
|
|
|
|
deploy_backup_everything(c, **everything_context)
|
2019-01-06 14:14:47 -06:00
|
|
|
|
|
|
|
# crontab
|
2019-09-13 13:23:09 -05:00
|
|
|
if runat is UNSPECIFIED:
|
|
|
|
runat = datetime.time(0) # defaults to midnight
|
|
|
|
if runat is not None and (install_rattail or everything):
|
2019-01-06 14:14:47 -06:00
|
|
|
crontab_context = dict(context)
|
|
|
|
crontab_context['envname'] = envname
|
|
|
|
crontab_context['pretty_time'] = runat.strftime('%I:%M %p')
|
|
|
|
crontab_context['cron_time'] = runat.strftime('%M %H')
|
2023-02-23 13:05:54 -06:00
|
|
|
|
|
|
|
if crontab_mailto:
|
|
|
|
if isinstance(crontab_mailto, str):
|
|
|
|
crontab_mailto = [crontab_mailto]
|
|
|
|
crontab_mailto = ','.join(crontab_mailto)
|
|
|
|
crontab_context['mailto'] = crontab_mailto
|
|
|
|
|
2019-01-06 14:14:47 -06:00
|
|
|
if crontab:
|
|
|
|
deploy(c, crontab, '/etc/cron.d/backup', context=crontab_context, use_sudo=True)
|
|
|
|
else:
|
|
|
|
deploy_generic(c, 'backup/crontab.mako', '/etc/cron.d/backup', context=crontab_context, use_sudo=True)
|
2023-02-17 11:01:29 -06:00
|
|
|
|
|
|
|
# logrotate
|
|
|
|
if logrotate:
|
|
|
|
deploy_generic(c, 'backup/logrotate.conf', '/etc/logrotate.d/backup',
|
|
|
|
use_sudo=True)
|