Compare commits
128 commits
Author | SHA1 | Date | |
---|---|---|---|
|
8bddce1329 | ||
|
542875a44c | ||
![]() |
8bd4236291 | ||
![]() |
00b0082302 | ||
![]() |
515ccc9f0c | ||
![]() |
e2ce4fd257 | ||
![]() |
fcfe741b51 | ||
![]() |
59ab46a335 | ||
![]() |
8ab21914bd | ||
![]() |
9eb44fa172 | ||
![]() |
65a9a7c9be | ||
![]() |
5ffcf0297c | ||
![]() |
7d031e1a41 | ||
![]() |
04c586a11e | ||
![]() |
d0425115da | ||
![]() |
8b5bb956ae | ||
![]() |
5c5038144f | ||
![]() |
014982028b | ||
![]() |
8c51ee8735 | ||
![]() |
4e3b8f6520 | ||
![]() |
be728e9bb3 | ||
![]() |
284e55c05c | ||
![]() |
88acb15c1c | ||
![]() |
8f8013aee0 | ||
![]() |
305e4c40c7 | ||
![]() |
6a15b5ae5e | ||
![]() |
c13e9ff23c | ||
![]() |
ae7cb45ab3 | ||
![]() |
24d632b7e3 | ||
![]() |
8e9a685006 | ||
![]() |
bedca74ca1 | ||
![]() |
e2369b1f53 | ||
![]() |
ab1baaa6d2 | ||
![]() |
94945fbc30 | ||
![]() |
6479af6a57 | ||
![]() |
075f931b5e | ||
![]() |
989f1574dc | ||
![]() |
d713bbe522 | ||
![]() |
64c69aab3c | ||
![]() |
9eab7a7555 | ||
![]() |
3f931445a0 | ||
![]() |
8bafaef796 | ||
![]() |
753e939422 | ||
![]() |
88e92588f2 | ||
![]() |
47bc4ce01d | ||
![]() |
1356bd0310 | ||
![]() |
86d922708a | ||
![]() |
cf32571c83 | ||
![]() |
21a071d324 | ||
![]() |
43d8df3c5d | ||
![]() |
2097136844 | ||
![]() |
20a3871522 | ||
![]() |
bdd89db30d | ||
![]() |
6c78d3c1d5 | ||
![]() |
cde124b916 | ||
![]() |
a76d38c201 | ||
![]() |
9e0a1cde19 | ||
![]() |
c8ab84e462 | ||
![]() |
492e8da5c3 | ||
![]() |
7d536499d6 | ||
![]() |
d68b57baa8 | ||
![]() |
6fb9389e45 | ||
![]() |
8c556e6176 | ||
![]() |
5fcfc91d83 | ||
![]() |
375cd1a36f | ||
![]() |
dc5556651f | ||
![]() |
cfba5425a1 | ||
![]() |
a3339ac062 | ||
![]() |
1fedc314b3 | ||
![]() |
a9bbee572a | ||
![]() |
ebf94fb7dc | ||
![]() |
5acf8d5304 | ||
![]() |
f1ecf9a1e4 | ||
![]() |
4c8e956fec | ||
![]() |
25891902b8 | ||
![]() |
a36ecdb2ad | ||
![]() |
7ce3aae20c | ||
![]() |
46440b6a95 | ||
![]() |
614fd92a20 | ||
![]() |
a2dca4ea65 | ||
![]() |
987731885b | ||
![]() |
ba05d616d8 | ||
![]() |
303f650a0b | ||
![]() |
f97401009e | ||
![]() |
5a4e2701df | ||
![]() |
5df704679f | ||
![]() |
a1b580e51a | ||
![]() |
097a96fc07 | ||
![]() |
fd0e55ab60 | ||
![]() |
a2f5542ed8 | ||
![]() |
6bf697da1d | ||
![]() |
ca59000287 | ||
![]() |
b58c0da7a4 | ||
![]() |
5b985fb803 | ||
![]() |
cbf93624e0 | ||
![]() |
955ceea801 | ||
![]() |
c413c2a1f2 | ||
![]() |
3287505f88 | ||
![]() |
1fd9ad48d6 | ||
![]() |
506b88d3e2 | ||
![]() |
9ae0015aba | ||
![]() |
9048403130 | ||
![]() |
f87f1e875c | ||
![]() |
70c5d661a3 | ||
![]() |
7500202af0 | ||
![]() |
8b8df95633 | ||
![]() |
ab822411be | ||
![]() |
a77b1ec100 | ||
![]() |
7963380715 | ||
![]() |
f5a7f80fd4 | ||
![]() |
8c08412d7b | ||
![]() |
47c27eaba5 | ||
![]() |
caf94ac65a | ||
![]() |
97ed8eeff0 | ||
![]() |
96f950d958 | ||
![]() |
d744257af6 | ||
![]() |
c67cc74d3d | ||
![]() |
c9aebd84e9 | ||
![]() |
c2cca7aa76 | ||
![]() |
7471dd1bf5 | ||
![]() |
e8821b3395 | ||
![]() |
19fcc94dc7 | ||
![]() |
6ebc5bbf59 | ||
![]() |
6d8cf3adb6 | ||
![]() |
6cf17a054b | ||
![]() |
6cb6ddaba7 | ||
![]() |
d6a11908ce | ||
![]() |
3d5b7c1d6e |
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -1,2 +1,4 @@
|
|||
*~
|
||||
*.pyc
|
||||
dist/
|
||||
rattail_fabric2.egg-info/
|
||||
|
|
53
CHANGELOG.md
Normal file
53
CHANGELOG.md
Normal file
|
@ -0,0 +1,53 @@
|
|||
|
||||
# Changelog
|
||||
All notable changes to rattail-fabric2 will be documented in this file.
|
||||
|
||||
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
|
||||
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## v0.4.5 (2025-02-01)
|
||||
|
||||
### Fix
|
||||
|
||||
- purge email settings for wuttjamaican also
|
||||
|
||||
## v0.4.4 (2024-10-03)
|
||||
|
||||
### Fix
|
||||
|
||||
- update project source links, kallithea -> forgejo
|
||||
|
||||
## v0.4.3 (2024-08-06)
|
||||
|
||||
### Fix
|
||||
|
||||
- setup basic log files for CORE Lane
|
||||
- avoid rich traceback for overnight luigi commands
|
||||
- avoid rich traceback for backup script
|
||||
- avoid rich traceback for overnight luigi commands
|
||||
- install more dependencies for borg
|
||||
- install `emacs-common-non-dfsg` only if available
|
||||
|
||||
## v0.4.2 (2024-07-05)
|
||||
|
||||
### Fix
|
||||
|
||||
- install non-dfsg package for emacs
|
||||
- remove references, dependency for `six` package
|
||||
|
||||
## v0.4.1 (2024-06-30)
|
||||
|
||||
### Fix
|
||||
|
||||
- always install `venv` pkg when bootstrapping python
|
||||
|
||||
## v0.4.0 (2024-06-10)
|
||||
|
||||
### Feat
|
||||
|
||||
- switch from setup.cfg to pyproject.toml + hatchling
|
||||
|
||||
|
||||
## Older Releases
|
||||
|
||||
Please see `docs/OLDCHANGES.rst` for older release notes.
|
20
CHANGES.rst
20
CHANGES.rst
|
@ -1,20 +0,0 @@
|
|||
|
||||
CHANGELOG
|
||||
=========
|
||||
|
||||
0.2.2 (2020-09-08)
|
||||
------------------
|
||||
|
||||
- Include all "deploy" files within manifest.
|
||||
|
||||
|
||||
0.2.1 (2020-09-08)
|
||||
------------------
|
||||
|
||||
- OMG so many changes. Just a "save point" more or less.
|
||||
|
||||
|
||||
0.2.0 (2018-12-03)
|
||||
------------------
|
||||
|
||||
- Initial release, somewhat forked from ``rattail-fabric`` package.
|
11
README.md
Normal file
11
README.md
Normal file
|
@ -0,0 +1,11 @@
|
|||
|
||||
# rattail-fabric2
|
||||
|
||||
Rattail is a retail software framework, released under the GNU General Public
|
||||
License.
|
||||
|
||||
This package contains various utility functions for use with
|
||||
[Fabric](http://www.fabfile.org/) (v2).
|
||||
|
||||
Please see Rattail's [home page](https://rattailproject.org/) for more
|
||||
information.
|
14
README.rst
14
README.rst
|
@ -1,14 +0,0 @@
|
|||
|
||||
rattail-fabric2
|
||||
===============
|
||||
|
||||
Rattail is a retail software framework, released under the GNU General Public
|
||||
License.
|
||||
|
||||
This package contains various utility functions for use with `Fabric`_ (v2).
|
||||
|
||||
.. _`Fabric`: http://www.fabfile.org/
|
||||
|
||||
Please see Rattail's `home page`_ for more information.
|
||||
|
||||
.. _`home page`: https://rattailproject.org/
|
95
docs/OLDCHANGES.rst
Normal file
95
docs/OLDCHANGES.rst
Normal file
|
@ -0,0 +1,95 @@
|
|||
|
||||
CHANGELOG
|
||||
=========
|
||||
|
||||
NB. this file contains "old" release notes only. for newer releases
|
||||
see the `CHANGELOG.md` file in the source root folder.
|
||||
|
||||
|
||||
0.3.6 (2024-05-31)
|
||||
------------------
|
||||
|
||||
* Bump version to fix PyPI upload.
|
||||
|
||||
|
||||
0.3.5 (2024-05-31)
|
||||
------------------
|
||||
|
||||
* Fix command line args in scripts, per typer.
|
||||
|
||||
|
||||
0.3.4 (2024-05-07)
|
||||
------------------
|
||||
|
||||
* Fix shell when creating new linux user account.
|
||||
|
||||
|
||||
0.3.3 (2023-09-25)
|
||||
------------------
|
||||
|
||||
* Add separate functions for dump, restore of mysql DB.
|
||||
|
||||
* Preserve correct owner for ``.bashrc`` when configuring node.js.
|
||||
|
||||
* Move sql file to temp path when restoring postgres db.
|
||||
|
||||
* Add ``clang`` workaround for pythonz.
|
||||
|
||||
* Add ``mysql.get_version_string()`` convenience function.
|
||||
|
||||
* Add option to skip raw SQL file when dumping postgres DB.
|
||||
|
||||
|
||||
0.3.2 (2023-06-10)
|
||||
------------------
|
||||
|
||||
* Let caller override default ``fannie/config.php``.
|
||||
|
||||
|
||||
0.3.1 (2023-06-10)
|
||||
------------------
|
||||
|
||||
* Touch ``fannie.log`` when installing CORE Office.
|
||||
|
||||
* Add password support for ``make_normal_user()``.
|
||||
|
||||
|
||||
0.3.0 (2023-06-08)
|
||||
------------------
|
||||
|
||||
- OMG so many changes...just needed a fresh release.
|
||||
|
||||
|
||||
0.2.4 (2020-09-25)
|
||||
------------------
|
||||
|
||||
- Allow kwargs for template context when deploying sudoers file.
|
||||
- Pass arbitrary kwargs along, for ``apt.install()``.
|
||||
- Add ``method`` kwarg for ``python.install_pip()``.
|
||||
- Require the 'rattail' package.
|
||||
- Add ``mssql`` module for installing MS SQL Server ODBC driver.
|
||||
- Add ``is_debian()`` convenience function.
|
||||
|
||||
|
||||
0.2.3 (2020-09-08)
|
||||
------------------
|
||||
|
||||
- Improve support for installing pip etc. on python3.
|
||||
|
||||
|
||||
0.2.2 (2020-09-08)
|
||||
------------------
|
||||
|
||||
- Include all "deploy" files within manifest.
|
||||
|
||||
|
||||
0.2.1 (2020-09-08)
|
||||
------------------
|
||||
|
||||
- OMG so many changes. Just a "save point" more or less.
|
||||
|
||||
|
||||
0.2.0 (2018-12-03)
|
||||
------------------
|
||||
|
||||
- Initial release, somewhat forked from ``rattail-fabric`` package.
|
41
pyproject.toml
Normal file
41
pyproject.toml
Normal file
|
@ -0,0 +1,41 @@
|
|||
|
||||
[build-system]
|
||||
requires = ["hatchling"]
|
||||
build-backend = "hatchling.build"
|
||||
|
||||
|
||||
[project]
|
||||
name = "rattail-fabric2"
|
||||
version = "0.4.5"
|
||||
description = "Fabric (v2) Utilities for Rattail"
|
||||
readme = "README.md"
|
||||
authors = [{name = "Lance Edgar", email = "lance@edbob.org"}]
|
||||
license = {text = "GNU GPL v3+"}
|
||||
classifiers = [
|
||||
"Development Status :: 3 - Alpha",
|
||||
"Environment :: Console",
|
||||
"Intended Audience :: Developers",
|
||||
"License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)",
|
||||
"Natural Language :: English",
|
||||
"Operating System :: OS Independent",
|
||||
"Programming Language :: Python :: 3",
|
||||
"Topic :: System :: Systems Administration",
|
||||
"Topic :: Software Development :: Libraries :: Python Modules",
|
||||
]
|
||||
dependencies = [
|
||||
"fabric2",
|
||||
"invoke",
|
||||
"rattail",
|
||||
]
|
||||
|
||||
|
||||
[project.urls]
|
||||
Homepage = "https://rattailproject.org"
|
||||
Repository = "https://forgejo.wuttaproject.org/rattail/rattail-fabric2"
|
||||
Changelog = "https://forgejo.wuttaproject.org/rattail/rattail-fabric2/src/branch/master/CHANGELOG.md"
|
||||
|
||||
|
||||
[tool.commitizen]
|
||||
version_provider = "pep621"
|
||||
tag_format = "v$version"
|
||||
update_changelog_on_bump = true
|
|
@ -2,7 +2,7 @@
|
|||
################################################################################
|
||||
#
|
||||
# Rattail -- Retail Software Framework
|
||||
# Copyright © 2010-2019 Lance Edgar
|
||||
# Copyright © 2010-2023 Lance Edgar
|
||||
#
|
||||
# This file is part of Rattail.
|
||||
#
|
||||
|
@ -27,15 +27,19 @@ This package contains various tasks and associated functions for use with
|
|||
Fabric deployment and maintenance.
|
||||
"""
|
||||
|
||||
from ._version import __version__
|
||||
|
||||
from .core import (
|
||||
is_debian,
|
||||
get_debian_version,
|
||||
get_ubuntu_version,
|
||||
Deployer,
|
||||
make_deploy,
|
||||
make_normal_user,
|
||||
make_system_user,
|
||||
mkdir,
|
||||
rsync,
|
||||
set_timezone,
|
||||
UNSPECIFIED,
|
||||
)
|
||||
from .util import exists, contains, append, sed
|
||||
from .util import exists, contains, append, sed, uncomment
|
||||
|
|
|
@ -1,3 +1,9 @@
|
|||
# -*- coding: utf-8; -*-
|
||||
|
||||
__version__ = '0.2.2'
|
||||
try:
|
||||
from importlib.metadata import version
|
||||
except ImportError:
|
||||
from importlib_metadata import version
|
||||
|
||||
|
||||
__version__ = version('rattail-fabric2')
|
||||
|
|
|
@ -73,7 +73,7 @@ def get_php_version(c):
|
|||
"""
|
||||
result = c.sudo('php --version')
|
||||
if not result.failed:
|
||||
match = re.match(r'^PHP (\d+\.\d+)\.\d+-', result.stdout)
|
||||
match = re.match(r'^PHP (\d+\.\d+)\.\d+', result.stdout)
|
||||
if match:
|
||||
return float(match.group(1))
|
||||
|
||||
|
|
|
@ -36,12 +36,12 @@ def install(c, *packages, **kwargs):
|
|||
"""
|
||||
Install one or more packages via ``apt-get install``.
|
||||
"""
|
||||
frontend = kwargs.get('frontend', 'noninteractive')
|
||||
target = kwargs.get('target_release')
|
||||
frontend = kwargs.pop('frontend', 'noninteractive')
|
||||
target = kwargs.pop('target_release', None)
|
||||
target = '--target-release={}'.format(target) if target else ''
|
||||
force_yes = ' --force-yes' if kwargs.get('force_yes') else ''
|
||||
c.sudo('DEBIAN_FRONTEND={} apt-get --assume-yes {}{} install {}'.format(
|
||||
frontend, target, force_yes, ' '.join(packages)))
|
||||
force_yes = ' --force-yes' if kwargs.pop('force_yes', False) else ''
|
||||
return c.sudo('DEBIAN_FRONTEND={} apt-get --assume-yes {}{} install {}'.format(
|
||||
frontend, target, force_yes, ' '.join(packages)), **kwargs)
|
||||
|
||||
|
||||
def purge(c, *packages):
|
||||
|
@ -67,6 +67,16 @@ def add_source(c, entry):
|
|||
update(c)
|
||||
|
||||
|
||||
def upgrade(c, frontend='noninteractive'):
|
||||
"""
|
||||
Perform an ``apt-get upgrade`` operation.
|
||||
"""
|
||||
options = ''
|
||||
if frontend == 'noninteractive':
|
||||
options = '--option Dpkg::Options::="--force-confdef" --option Dpkg::Options::="--force-confold"'
|
||||
c.sudo('DEBIAN_FRONTEND={} apt-get --assume-yes {} upgrade'.format(frontend, options))
|
||||
|
||||
|
||||
def dist_upgrade(c, frontend='noninteractive'):
|
||||
"""
|
||||
Perform a full ``apt-get dist-upgrade`` operation.
|
||||
|
@ -102,4 +112,10 @@ def install_emacs(c):
|
|||
if ubuntu_version and ubuntu_version < 16:
|
||||
emacs = 'emacs23-nox'
|
||||
|
||||
install(c, emacs, 'emacs-goodies-el')
|
||||
install(c, emacs,
|
||||
'emacs-goodies-el',
|
||||
)
|
||||
|
||||
# nb. this includes tramp manual
|
||||
if c.sudo('dpkg -s emacs-common-non-dfsg', warn=True).ok:
|
||||
install(c, 'emacs-common-non-dfsg')
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
################################################################################
|
||||
#
|
||||
# Rattail -- Retail Software Framework
|
||||
# Copyright © 2010-2019 Lance Edgar
|
||||
# Copyright © 2010-2023 Lance Edgar
|
||||
#
|
||||
# This file is part of Rattail.
|
||||
#
|
||||
|
@ -53,7 +53,9 @@ def deploy_backup_everything(c, **context):
|
|||
|
||||
|
||||
def deploy_backup_app(c, deploy, envname, mkvirtualenv=True, user='rattail',
|
||||
python_exe='/usr/bin/python3',
|
||||
install_borg=False,
|
||||
pyfuse3=False,
|
||||
link_borg_to_bin=True,
|
||||
install_luigi=False,
|
||||
luigi_history_db=None,
|
||||
|
@ -63,7 +65,9 @@ def deploy_backup_app(c, deploy, envname, mkvirtualenv=True, user='rattail',
|
|||
rattail_backup_script=None,
|
||||
everything=None,
|
||||
crontab=None,
|
||||
crontab_mailto=None,
|
||||
runat=UNSPECIFIED,
|
||||
logrotate=False,
|
||||
context={}):
|
||||
"""
|
||||
Make an app which can run backups for the server.
|
||||
|
@ -77,7 +81,8 @@ def deploy_backup_app(c, deploy, envname, mkvirtualenv=True, user='rattail',
|
|||
if deploy.local_exists(path):
|
||||
config = path
|
||||
else:
|
||||
raise ValueError("Must provide config path for backup app")
|
||||
raise ValueError("Config file not found for backup; "
|
||||
"please add {} to your deploy folder".format(path))
|
||||
|
||||
if install_borg:
|
||||
borg.install_dependencies(c)
|
||||
|
@ -86,27 +91,37 @@ def deploy_backup_app(c, deploy, envname, mkvirtualenv=True, user='rattail',
|
|||
c.sudo('supervisorctl stop backup:')
|
||||
|
||||
# virtualenv
|
||||
if mkvirtualenv:
|
||||
python.mkvirtualenv(c, envname, python='/usr/bin/python3', runas_user=user)
|
||||
envpath = '/srv/envs/{}'.format(envname)
|
||||
c.sudo('chown -R {}: {}'.format(user, envpath))
|
||||
mkdir(c, os.path.join(envpath, 'src'), use_sudo=True, runas_user=user)
|
||||
c.sudo("bash -l -c 'workon {} && pip install --upgrade pip'".format(envname), user=user)
|
||||
if mkvirtualenv and not exists(c, envpath):
|
||||
mkdir(c, envpath, use_sudo=True, owner=user)
|
||||
python.mkvirtualenv(c, envname, python=python_exe, runas_user=user)
|
||||
c.sudo("bash -c 'PIP_CONFIG_FILE={0}/pip.conf {0}/bin/pip install -U pip setuptools wheel'".format(envpath),
|
||||
user=user)
|
||||
|
||||
if install_rattail:
|
||||
|
||||
# rattail
|
||||
mkdir(c, os.path.join(envpath, 'src'), use_sudo=True, runas_user=user)
|
||||
if not exists(c, os.path.join(envpath, 'src/rattail')):
|
||||
c.sudo('git clone https://rattailproject.org/git/rattail.git {}/src/rattail'.format(envpath), user=user)
|
||||
c.sudo("bash -l -c 'workon {} && cdvirtualenv && bin/pip install --editable src/rattail'".format(envname), user=user)
|
||||
c.sudo(f'git clone https://forgejo.wuttaproject.org/rattail/rattail.git {envpath}/src/rattail', user=user)
|
||||
c.sudo("bash -c 'PIP_CONFIG_FILE={0}/pip.conf {0}/bin/pip install --editable {0}/src/rattail'".format(envpath),
|
||||
user=user)
|
||||
deploy_generic(c, 'backup/git-exclude', os.path.join(envpath, 'src/rattail/.git/info/exclude'), use_sudo=True, owner=user)
|
||||
|
||||
# config
|
||||
c.sudo("bash -l -c 'workon {} && cdvirtualenv && rattail make-appdir'".format(envname), user=user)
|
||||
if not exists(c, os.path.join(envpath, 'app')):
|
||||
c.sudo("bash -c 'cd {} && bin/rattail make-appdir'".format(envpath),
|
||||
user=user)
|
||||
# note, config is owned by root regardless of `user` - since we always run backups as root
|
||||
deploy(c, config, os.path.join(envpath, 'app/rattail.conf'), owner='root:{}'.format(user), mode='0640', use_sudo=True, context=context)
|
||||
c.sudo("bash -l -c 'workon {} && cdvirtualenv && bin/rattail -c app/rattail.conf make-config -T quiet -O app/'".format(envname), user=user)
|
||||
c.sudo("bash -l -c 'workon {} && cdvirtualenv && bin/rattail -c app/rattail.conf make-config -T silent -O app/'".format(envname), user=user)
|
||||
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')):
|
||||
c.sudo("bash -c 'cd {} && bin/rattail -c app/rattail.conf make-config -T silent -O app/'".format(envpath),
|
||||
user=user)
|
||||
|
||||
# rattail-backup script
|
||||
script_context = dict(context)
|
||||
|
@ -119,18 +134,20 @@ def deploy_backup_app(c, deploy, envname, mkvirtualenv=True, user='rattail',
|
|||
|
||||
# borg
|
||||
if install_borg:
|
||||
if install_rattail:
|
||||
packages = [
|
||||
'rattail[backup]',
|
||||
]
|
||||
if isinstance(install_borg, list):
|
||||
packages = install_borg
|
||||
elif isinstance(install_borg, str):
|
||||
packages = [install_borg]
|
||||
else:
|
||||
# these should be same as rattail[backup]
|
||||
packages = [
|
||||
'msgpack',
|
||||
'borgbackup',
|
||||
'llfuse==1.3.4',
|
||||
]
|
||||
c.sudo("bash -l -c 'workon {} && cdvirtualenv && bin/pip install {}'".format(envname, ' '.join(packages)), user=user)
|
||||
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)),
|
||||
user=user)
|
||||
if link_borg_to_bin:
|
||||
c.sudo("ln -sf {}/bin/borg /usr/local/bin/borg".format(envpath))
|
||||
|
||||
|
@ -141,7 +158,8 @@ def deploy_backup_app(c, deploy, envname, mkvirtualenv=True, user='rattail',
|
|||
packages.append('SQLAlchemy')
|
||||
if luigi_history_db.startswith('postgresql://'):
|
||||
packages.append('psycopg2')
|
||||
c.sudo("bash -l -c 'workon {}; pip install {}'".format(envname, ' '.join(packages)), user=user)
|
||||
c.sudo("bash -c 'PIP_CONFIG_FILE={0}/pip.conf {0}/bin/pip install {1}'".format(envpath, ' '.join(packages)),
|
||||
user=user)
|
||||
|
||||
# basic config
|
||||
mkdir(c, ['{}/app/luigitasks'.format(envpath),
|
||||
|
@ -208,7 +226,19 @@ def deploy_backup_app(c, deploy, envname, mkvirtualenv=True, user='rattail',
|
|||
crontab_context['envname'] = envname
|
||||
crontab_context['pretty_time'] = runat.strftime('%I:%M %p')
|
||||
crontab_context['cron_time'] = runat.strftime('%M %H')
|
||||
|
||||
if crontab_mailto:
|
||||
if isinstance(crontab_mailto, str):
|
||||
crontab_mailto = [crontab_mailto]
|
||||
crontab_mailto = ','.join(crontab_mailto)
|
||||
crontab_context['mailto'] = crontab_mailto
|
||||
|
||||
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)
|
||||
|
||||
# logrotate
|
||||
if logrotate:
|
||||
deploy_generic(c, 'backup/logrotate.conf', '/etc/logrotate.d/backup',
|
||||
use_sudo=True)
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
################################################################################
|
||||
#
|
||||
# Rattail -- Retail Software Framework
|
||||
# Copyright © 2010-2018 Lance Edgar
|
||||
# Copyright © 2010-2024 Lance Edgar
|
||||
#
|
||||
# This file is part of Rattail.
|
||||
#
|
||||
|
@ -26,8 +26,6 @@ Fabric library for Borg backups
|
|||
https://www.borgbackup.org/
|
||||
"""
|
||||
|
||||
from __future__ import unicode_literals, absolute_import
|
||||
|
||||
from rattail_fabric2 import apt
|
||||
|
||||
|
||||
|
@ -39,6 +37,9 @@ def install_dependencies(c):
|
|||
c,
|
||||
'libacl1-dev',
|
||||
'libfuse-dev',
|
||||
'liblz4-dev',
|
||||
'libssl-dev',
|
||||
'libxxhash-dev',
|
||||
'libzstd-dev',
|
||||
'pkg-config',
|
||||
)
|
||||
|
|
41
rattail_fabric2/byjove.py
Normal file
41
rattail_fabric2/byjove.py
Normal file
|
@ -0,0 +1,41 @@
|
|||
# -*- coding: utf-8; -*-
|
||||
################################################################################
|
||||
#
|
||||
# Rattail -- Retail Software Framework
|
||||
# Copyright © 2010-2023 Lance Edgar
|
||||
#
|
||||
# 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 Byjove
|
||||
"""
|
||||
|
||||
from rattail_fabric2 import exists, mkdir
|
||||
|
||||
|
||||
def install_from_source(c, user='rattail'):
|
||||
"""
|
||||
Install the 'byjove' source code package, for staging systems
|
||||
"""
|
||||
if not exists(c, '/usr/local/src/byjove'):
|
||||
mkdir(c, '/usr/local/src/byjove', use_sudo=True, owner=user)
|
||||
c.sudo('git clone https://forgejo.wuttaproject.org/rattail/byjove.git /usr/local/src/byjove',
|
||||
user=user)
|
||||
c.sudo("bash -l -c 'cd /usr/local/src/byjove; npm link'",
|
||||
user=user)
|
||||
c.sudo("bash -l -c 'cd /usr/local/src/byjove; npm run build'",
|
||||
user=user)
|
69
rattail_fabric2/collectd.py
Normal file
69
rattail_fabric2/collectd.py
Normal file
|
@ -0,0 +1,69 @@
|
|||
# -*- coding: utf-8; -*-
|
||||
################################################################################
|
||||
#
|
||||
# Rattail -- Retail Software Framework
|
||||
# Copyright © 2010-2023 Lance Edgar
|
||||
#
|
||||
# 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 collectd
|
||||
"""
|
||||
|
||||
from rattail_fabric2 import apt, make_deploy, sed
|
||||
|
||||
|
||||
deploy = make_deploy(__file__)
|
||||
|
||||
|
||||
def install_collectd(c, interval=None, rrdtool=None, restart_=False):
|
||||
"""
|
||||
Install the ``collectd`` service.
|
||||
|
||||
:param interval: Optional override for the collectd ``Interval``
|
||||
setting.
|
||||
"""
|
||||
apt.install(c, 'collectd')
|
||||
|
||||
if interval:
|
||||
sed(c, '/etc/collectd/collectd.conf',
|
||||
r'^#? ?Interval\s+[0-9]+.*$',
|
||||
'Interval {}'.format(interval),
|
||||
use_sudo=True)
|
||||
|
||||
if rrdtool is not None:
|
||||
sed(c, '/etc/collectd/collectd.conf',
|
||||
r'^#? ?LoadPlugin rrdtool\s*$',
|
||||
'{}LoadPlugin rrdtool'.format('' if rrdtool else '#'),
|
||||
use_sudo=True)
|
||||
|
||||
if restart_:
|
||||
restart(c)
|
||||
|
||||
|
||||
def restart(c):
|
||||
"""
|
||||
Restart the collectd service.
|
||||
"""
|
||||
c.sudo('systemctl restart collectd')
|
||||
|
||||
|
||||
def deploy_mountpoint_check_script(c, dest, **kwargs):
|
||||
"""
|
||||
Deploy generic mountpoint check script to specified destination.
|
||||
"""
|
||||
deploy(c, 'collectd/check-mountpoints.py', dest, **kwargs)
|
|
@ -2,7 +2,7 @@
|
|||
################################################################################
|
||||
#
|
||||
# Rattail -- Retail Software Framework
|
||||
# Copyright © 2010-2019 Lance Edgar
|
||||
# Copyright © 2010-2022 Lance Edgar
|
||||
#
|
||||
# This file is part of Rattail.
|
||||
#
|
||||
|
@ -24,14 +24,19 @@
|
|||
Fabric lib for Composer (PHP dependency manager)
|
||||
"""
|
||||
|
||||
from __future__ import unicode_literals, absolute_import
|
||||
|
||||
from rattail_fabric2 import make_deploy, exists
|
||||
from rattail_fabric2 import apt, make_deploy, exists
|
||||
|
||||
|
||||
deploy = make_deploy(__file__)
|
||||
|
||||
|
||||
def install(c, with_apt=True):
|
||||
if with_apt:
|
||||
apt.install(c, 'composer')
|
||||
else:
|
||||
install_globally(c)
|
||||
|
||||
|
||||
def install_globally(c):
|
||||
"""
|
||||
Install `composer.phar` in global location
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
################################################################################
|
||||
#
|
||||
# Rattail -- Retail Software Framework
|
||||
# Copyright © 2010-2019 Lance Edgar
|
||||
# Copyright © 2010-2023 Lance Edgar
|
||||
#
|
||||
# This file is part of Rattail.
|
||||
#
|
||||
|
@ -27,6 +27,7 @@ Core Utilities
|
|||
import os
|
||||
import re
|
||||
import tempfile
|
||||
import warnings
|
||||
|
||||
from mako.template import Template
|
||||
|
||||
|
@ -64,6 +65,27 @@ def is_link(c, path, use_sudo=False):
|
|||
return False if result.failed else True
|
||||
|
||||
|
||||
def get_os_release_id(c):
|
||||
"""
|
||||
Return the "ID" field from `/etc/os-release` file
|
||||
"""
|
||||
contents = c.run('cat /etc/os-release').stdout.strip()
|
||||
data = {}
|
||||
for line in contents.split('\n'):
|
||||
field, value = line.split('=')
|
||||
data[field] = value
|
||||
return data['ID']
|
||||
|
||||
|
||||
def is_debian(c):
|
||||
"""
|
||||
Return boolean indicating if this machine is Debian (proper, as opposed to
|
||||
some derivative).
|
||||
"""
|
||||
release_id = get_os_release_id(c)
|
||||
return release_id == 'debian'
|
||||
|
||||
|
||||
def get_debian_version(c):
|
||||
"""
|
||||
Fetch the version of Debian running on the target system.
|
||||
|
@ -106,7 +128,37 @@ def mkdir(c, paths, owner=None, mode=None,
|
|||
func('chmod {} {}'.format(mode, ' '.join(paths)))
|
||||
|
||||
|
||||
def make_system_user(c, name, home=None, uid=None, shell=None):
|
||||
def make_normal_user(c, username, full_name=None,
|
||||
shell='/bin/bash',
|
||||
password=None,
|
||||
disabled_login=False):
|
||||
"""
|
||||
Make a new "normal" user account.
|
||||
|
||||
:param disabled_login: If true, will leave the account in a
|
||||
non-usable state, i.e. with invalid shell.
|
||||
"""
|
||||
# do not bother if user exists
|
||||
missing = c.run(f'getent passwd {username}', warn=True).failed
|
||||
if not missing:
|
||||
return
|
||||
|
||||
# nb. specify --disabled-login to avoid being prompted for password
|
||||
c.sudo("adduser --gecos '{}' --disabled-login {}".format(full_name or username,
|
||||
username))
|
||||
|
||||
# then fix the shell unless we shouldn't
|
||||
if not disabled_login:
|
||||
c.sudo(f'usermod -s {shell} {username}')
|
||||
|
||||
# and maybe set password
|
||||
if password:
|
||||
c.sudo(f"bash -c 'echo {username}:{password} | chpasswd'",
|
||||
echo=False)
|
||||
|
||||
|
||||
def make_system_user(c, name, home=None, uid=None, shell=None,
|
||||
disabled_password=None):
|
||||
"""
|
||||
Make a new system user account, with the given home folder and shell path.
|
||||
"""
|
||||
|
@ -116,7 +168,9 @@ def make_system_user(c, name, home=None, uid=None, shell=None):
|
|||
home = '--home {}'.format(home) if home else ''
|
||||
uid = '--uid {}'.format(uid) if uid else ''
|
||||
shell = '--shell {}'.format(shell) if shell else ''
|
||||
c.sudo('adduser --system --group {} {} {} {}'.format(name, home, uid, shell))
|
||||
disabled_password = '--disabled-password' if disabled_password else ''
|
||||
c.sudo('adduser --system --group {} {} {} {} {}'.format(name, home, uid, shell,
|
||||
disabled_password))
|
||||
|
||||
|
||||
def set_timezone(c, timezone):
|
||||
|
@ -193,6 +247,8 @@ class Deployer(object):
|
|||
self.deploy(c, local_path, remote_path, **kwargs)
|
||||
|
||||
def full_path(self, local_path):
|
||||
if local_path.startswith('/'):
|
||||
return local_path
|
||||
return '{}/{}'.format(self.deploy_path, local_path)
|
||||
|
||||
def local_exists(self, local_path):
|
||||
|
@ -208,7 +264,7 @@ class Deployer(object):
|
|||
put(c, local_path, remote_path, **kwargs)
|
||||
|
||||
def sudoers(self, c, local_path, remote_path, owner='root:', mode='0440', **kwargs):
|
||||
self.deploy(c, local_path, '/tmp/sudoers', owner=owner, mode=mode, use_sudo=True)
|
||||
self.deploy(c, local_path, '/tmp/sudoers', owner=owner, mode=mode, use_sudo=True, **kwargs)
|
||||
c.sudo('mv /tmp/sudoers {}'.format(remote_path))
|
||||
|
||||
def apache_site(self, c, local_path, name, **kwargs):
|
||||
|
@ -225,7 +281,8 @@ class Deployer(object):
|
|||
from rattail_fabric2.backup import deploy_backup_app
|
||||
deploy_backup_app(c, self, envname, *args, **kwargs)
|
||||
|
||||
def certbot_account(self, c, uuid, localdir='certbot/account'):
|
||||
def certbot_account(self, c, uuid, localdir='certbot/account',
|
||||
version='01'):
|
||||
"""
|
||||
Deploy files to establish a certbot account on target server
|
||||
"""
|
||||
|
@ -234,8 +291,8 @@ class Deployer(object):
|
|||
localdir = localdir.rstrip('/')
|
||||
paths = [
|
||||
'/etc/letsencrypt/accounts',
|
||||
'/etc/letsencrypt/accounts/acme-v01.api.letsencrypt.org',
|
||||
'/etc/letsencrypt/accounts/acme-v01.api.letsencrypt.org/directory',
|
||||
'/etc/letsencrypt/accounts/acme-v{}.api.letsencrypt.org'.format(version),
|
||||
'/etc/letsencrypt/accounts/acme-v{}.api.letsencrypt.org/directory'.format(version),
|
||||
]
|
||||
final_path = '{}/{}'.format(paths[-1], uuid)
|
||||
paths.append(final_path)
|
||||
|
@ -249,9 +306,13 @@ class Deployer(object):
|
|||
"""
|
||||
Deploy a "soffice" (headless LibreOffice) daemon.
|
||||
"""
|
||||
warnings.warn("deploy.soffice_daemon() is deprecated as it only supports "
|
||||
"/etc/init.d scripts. please deploy your own systemd config "
|
||||
"instead, as needed.", DeprecationWarning)
|
||||
if name is None:
|
||||
name = local_path.split('/')[-1]
|
||||
kwargs.setdefault('use_sudo', True)
|
||||
kwargs.setdefault('mode', '0755')
|
||||
self.deploy(c, local_path, '/etc/init.d/{}'.format(name), **kwargs)
|
||||
if register:
|
||||
c.sudo('update-rc.d {} defaults'.format(name))
|
||||
|
@ -309,13 +370,21 @@ def make_deploy(deploy_path, last_segment='deploy'):
|
|||
return Deployer(deploy_path, last_segment)
|
||||
|
||||
|
||||
def rsync(c, host, *paths):
|
||||
def rsync(c, host, *paths, files=False, target=None):
|
||||
"""
|
||||
Runs rsync as root, for the given host and file paths.
|
||||
|
||||
:param files: If set, this indicates that each of the ``paths``
|
||||
are actual files, as opposed to directories.
|
||||
|
||||
:param target: If set, this will be used instead of the given
|
||||
path, for destination path. This only works correctly if
|
||||
``paths`` contains only one path.
|
||||
"""
|
||||
for path in paths:
|
||||
assert path.startswith('/')
|
||||
path = path.rstrip('/')
|
||||
# escape path for rsync
|
||||
path = path.replace(' ', r'\\\ ').replace("'", r"\\\'")
|
||||
agent_sudo(c, 'rsync -aP --del root@{0}:{1}/ {1}'.format(host, path))
|
||||
suffix = '' if files else '/'
|
||||
agent_sudo(c, f'rsync -aP --del root@{host}:{path}{suffix} {target or path}')
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
################################################################################
|
||||
#
|
||||
# Rattail -- Retail Software Framework
|
||||
# Copyright © 2010-2019 Lance Edgar
|
||||
# Copyright © 2010-2023 Lance Edgar
|
||||
#
|
||||
# This file is part of Rattail.
|
||||
#
|
||||
|
@ -24,13 +24,103 @@
|
|||
Fabric library for CORE-POS (IS4C)
|
||||
"""
|
||||
|
||||
from __future__ import unicode_literals, absolute_import
|
||||
|
||||
import os
|
||||
|
||||
from rattail_fabric2 import mysql, exists, mkdir
|
||||
from rattail_fabric2 import mysql, exists, make_deploy, mkdir
|
||||
|
||||
|
||||
deploy_generic = make_deploy(__file__)
|
||||
|
||||
|
||||
def install_corepos(c, rootdir, rooturl_office, production=True,
|
||||
user='www-data',
|
||||
repo='https://github.com/CORE-POS/IS4C.git',
|
||||
branch='master',
|
||||
mysql_username='corepos',
|
||||
mysql_password='corepos',
|
||||
mysql_name_prefix='',
|
||||
composer='composer.phar',
|
||||
composer_install=True,
|
||||
make_shadowread=False,
|
||||
fannie_config=True):
|
||||
"""
|
||||
Install the CORE software to the given location.
|
||||
|
||||
This will clone CORE code to the ``IS4C`` folder within the
|
||||
specified ``rootdir`` location.
|
||||
"""
|
||||
rooturl_office = rooturl_office.rstrip('/')
|
||||
|
||||
mkdir(c, rootdir, use_sudo=True, owner=user)
|
||||
|
||||
# CORE source
|
||||
is4c = os.path.join(rootdir, 'IS4C')
|
||||
if not exists(c, is4c):
|
||||
c.sudo('git clone --branch {} {} {}'.format(branch, repo, is4c),
|
||||
user=user)
|
||||
if production:
|
||||
c.sudo('rm -f {}/fannie/DEV_MODE'.format(is4c), user=user)
|
||||
else:
|
||||
c.sudo('touch {}/fannie/DEV_MODE'.format(is4c), user=user)
|
||||
|
||||
# composer install
|
||||
if composer_install:
|
||||
# TODO: these 'allow' entries are needed for composer 2.4 at least...
|
||||
c.sudo("bash -c 'cd {} && {} config --global --no-plugins allow-plugins.composer/installers true'".format(is4c, composer),
|
||||
user=user)
|
||||
c.sudo("bash -c 'cd {} && {} config --global --no-plugins allow-plugins.oomphinc/composer-installers-extender true'".format(is4c, composer),
|
||||
user=user)
|
||||
c.sudo("bash -c 'cd {} && {} config --global --no-plugins allow-plugins.corepos/composer-installer true'".format(is4c, composer),
|
||||
user=user)
|
||||
c.sudo("bash -c 'cd {} && {} install'".format(is4c, composer),
|
||||
user=user)
|
||||
# TODO: (why) is 'update' needed instead of 'install' ?
|
||||
# c.sudo("bash -c 'cd {} && {} update'".format(is4c, composer),
|
||||
# user=user)
|
||||
|
||||
# shadowread
|
||||
if make_shadowread:
|
||||
c.sudo("bash -c 'cd {}/fannie/auth/shadowread && make'".format(is4c),
|
||||
user=user)
|
||||
# nb. must run `make install` as root
|
||||
c.sudo("bash -c 'cd {}/fannie/auth/shadowread && make install'".format(is4c))
|
||||
|
||||
# fannie databases
|
||||
mysql.create_db(c, '{}core_op'.format(mysql_name_prefix),
|
||||
user='{}@localhost'.format(mysql_username))
|
||||
mysql.create_db(c, '{}core_trans'.format(mysql_name_prefix),
|
||||
user='{}@localhost'.format(mysql_username))
|
||||
mysql.create_db(c, '{}trans_archive'.format(mysql_name_prefix),
|
||||
user='{}@localhost'.format(mysql_username))
|
||||
|
||||
# fannie config
|
||||
if fannie_config:
|
||||
remote_path = '{}/IS4C/fannie/config.php'.format(rootdir)
|
||||
if not exists(c, remote_path):
|
||||
if fannie_config is True:
|
||||
fannie_config = 'corepos/fannie-config.php.mako'
|
||||
deploy_generic(c, fannie_config, remote_path,
|
||||
use_sudo=True, owner='www-data:{}'.format(user), mode='0640',
|
||||
context={'rootdir': rootdir,
|
||||
'rooturl': rooturl_office,
|
||||
'mysql_username': mysql_username,
|
||||
'mysql_password': mysql_password,
|
||||
'mysql_name_prefix': mysql_name_prefix})
|
||||
|
||||
# office logging
|
||||
mkdir(c, f'{is4c}/fannie/logs', use_sudo=True,
|
||||
owner=f'{user}:www-data', mode='0775')
|
||||
c.sudo(f"bash -c 'cd {is4c}/fannie/logs && touch fannie.log debug_fannie.log'",
|
||||
user='www-data')
|
||||
|
||||
# lane logging
|
||||
mkdir(c, f'{is4c}/pos/is4c-nf/log', use_sudo=True,
|
||||
owner=f'{user}:www-data', mode='0775')
|
||||
c.sudo(f"bash -c 'cd {is4c}/pos/is4c-nf/log && touch lane.log debug_lane.log'",
|
||||
user='www-data')
|
||||
|
||||
|
||||
# TODO: deprecate / remove this
|
||||
def install_fannie(c, rootdir, user='www-data', branch='version-2.10',
|
||||
mysql_user='is4c', mysql_pass='is4c'):
|
||||
"""
|
||||
|
|
|
@ -11,6 +11,8 @@ else
|
|||
fi
|
||||
|
||||
RATTAIL="/srv/envs/${envname}/bin/rattail --config=$CONFIG $PROGRESS $VERBOSE"
|
||||
# nb. avoid rich-formatted traceback here since it's so "noisy"
|
||||
export _TYPER_STANDARD_TRACEBACK=1
|
||||
|
||||
|
||||
# sanity check
|
||||
|
|
|
@ -1,4 +1,8 @@
|
|||
# -*- mode: conf; -*-
|
||||
|
||||
% if mailto:
|
||||
MAILTO="${mailto}"
|
||||
% endif
|
||||
|
||||
# backup everything of importance at ${pretty_time}
|
||||
${'' if env.machine_is_live else '# '}${cron_time} * * * root /usr/local/bin/backup-everything
|
||||
|
|
10
rattail_fabric2/deploy/backup/logrotate.conf
Normal file
10
rattail_fabric2/deploy/backup/logrotate.conf
Normal file
|
@ -0,0 +1,10 @@
|
|||
|
||||
/srv/envs/backup/app/log/rattail.log {
|
||||
daily
|
||||
missingok
|
||||
rotate 30
|
||||
compress
|
||||
delaycompress
|
||||
notifempty
|
||||
create 600 rattail rattail
|
||||
}
|
|
@ -3,16 +3,18 @@
|
|||
if [ "$1" = "-v" -o "$1" = "--verbose" ]; then
|
||||
VERBOSE='--verbose'
|
||||
QUIET=
|
||||
PROGRESSBAR='--progress-bar=on'
|
||||
else
|
||||
VERBOSE=
|
||||
QUIET='--quiet'
|
||||
PROGRESSBAR='--progress-bar=off'
|
||||
fi
|
||||
|
||||
PIP="sudo -H -u ${user} PIP_CONFIG_FILE=${envpath}/pip.conf ${envpath}/bin/pip"
|
||||
|
||||
|
||||
# upgrade pip
|
||||
$PIP install $QUIET --upgrade pip
|
||||
$PIP install $QUIET $PROGRESSBAR --upgrade pip
|
||||
|
||||
# upgrade rattail
|
||||
cd ${envpath}/src/rattail
|
||||
|
@ -22,4 +24,4 @@ if [ "$(sudo -H -u ${user} git status --porcelain)" != '' ]; then
|
|||
fi
|
||||
sudo -H -u ${user} git pull $QUIET
|
||||
find . -name '*.pyc' -delete
|
||||
$PIP install $QUIET --upgrade --upgrade-strategy eager --editable .
|
||||
$PIP install $QUIET $PROGRESSBAR --upgrade --upgrade-strategy eager --editable .
|
||||
|
|
|
@ -1,142 +0,0 @@
|
|||
#!/bin/sh
|
||||
# -*- coding: utf-8; -*-
|
||||
################################################################################
|
||||
#
|
||||
# Rattail -- Retail Software Framework
|
||||
# Copyright © 2010-2018 Lance Edgar
|
||||
#
|
||||
# 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/>.
|
||||
#
|
||||
################################################################################
|
||||
|
||||
|
||||
# This script is mostly based on the ``/etc/init.d/skeleton`` file from a
|
||||
# Debian 6 system.
|
||||
|
||||
|
||||
DESC=${DESC:-"Rattail Email Bouncer"}
|
||||
NAME=${NAME:-"rattail-bouncer"}
|
||||
SCRIPTNAME=${SCRIPTNAME:-"/etc/init.d/$NAME"}
|
||||
PIDFILE=${PIDFILE:-"/var/run/rattail/$NAME.pid"}
|
||||
|
||||
PYTHON=${PYTHON:-"/usr/bin/python"}
|
||||
RATTAIL=${RATTAIL:-"/usr/local/bin/rattail"}
|
||||
RATTAIL_ARGS=${RATTAIL_ARGS:-""}
|
||||
BOUNCER_ARGS=${BOUNCER_ARGS:-"--pidfile=$PIDFILE"}
|
||||
|
||||
USER=${USER:-"rattail"}
|
||||
GROUP=${GROUP:-"rattail"}
|
||||
|
||||
|
||||
# Read configuration variable files if present.
|
||||
[ -r /etc/default/rattail ] && . /etc/default/rattail
|
||||
[ -r /etc/default/$NAME ] && . /etc/default/$NAME
|
||||
|
||||
# Load the VERBOSE setting and other rcS variables.
|
||||
. /lib/init/vars.sh
|
||||
|
||||
# Define LSB log_* functions.
|
||||
# Depend on lsb-base (>= 3.2-14) to ensure that this file is present
|
||||
# and status_of_proc is working.
|
||||
. /lib/lsb/init-functions
|
||||
|
||||
|
||||
create_pid_dir() {
|
||||
PIDDIR=`dirname "$PIDFILE"`
|
||||
if [ ! -d "$PIDDIR" ]; then
|
||||
mkdir --parents "$PIDDIR"
|
||||
fi
|
||||
chown $USER:$GROUP "$PIDDIR"
|
||||
}
|
||||
|
||||
|
||||
#
|
||||
# Function that starts the daemon/service
|
||||
#
|
||||
do_start()
|
||||
{
|
||||
# Return
|
||||
# 0 if daemon has been started
|
||||
# 1 if daemon was already running
|
||||
# 2 if daemon could not be started
|
||||
create_pid_dir
|
||||
start-stop-daemon --start --pidfile $PIDFILE --exec $PYTHON --user $USER --test --quiet > /dev/null \
|
||||
|| return 1
|
||||
start-stop-daemon --start --pidfile $PIDFILE --exec $PYTHON --startas $RATTAIL --chuid $USER --group $GROUP --quiet -- \
|
||||
$RATTAIL_ARGS bouncer $BOUNCER_ARGS start \
|
||||
|| return 2
|
||||
}
|
||||
|
||||
|
||||
#
|
||||
# Function that stops the daemon/service
|
||||
#
|
||||
do_stop()
|
||||
{
|
||||
# Return
|
||||
# 0 if daemon has been stopped
|
||||
# 1 if daemon was already stopped
|
||||
# 2 if daemon could not be stopped
|
||||
# other if a failure occurred
|
||||
sudo -u $USER $RATTAIL $RATTAIL_ARGS bouncer $BOUNCER_ARGS stop > /dev/null 2>&1
|
||||
}
|
||||
|
||||
|
||||
case "$1" in
|
||||
start)
|
||||
[ "$VERBOSE" != no ] && log_daemon_msg "Starting $DESC" "$NAME"
|
||||
do_start
|
||||
case "$?" in
|
||||
0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;;
|
||||
2) [ "$VERBOSE" != no ] && log_end_msg 1 ;;
|
||||
esac
|
||||
;;
|
||||
stop)
|
||||
[ "$VERBOSE" != no ] && log_daemon_msg "Stopping $DESC" "$NAME"
|
||||
do_stop
|
||||
case "$?" in
|
||||
0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;;
|
||||
2) [ "$VERBOSE" != no ] && log_end_msg 1 ;;
|
||||
esac
|
||||
;;
|
||||
status)
|
||||
status_of_proc "$DAEMON" "$NAME" && exit 0 || exit $?
|
||||
;;
|
||||
restart|force-reload)
|
||||
log_daemon_msg "Restarting $DESC" "$NAME"
|
||||
do_stop
|
||||
case "$?" in
|
||||
0|1)
|
||||
do_start
|
||||
case "$?" in
|
||||
0) log_end_msg 0 ;;
|
||||
1) log_end_msg 1 ;; # Old process is still running
|
||||
*) log_end_msg 1 ;; # Failed to start
|
||||
esac
|
||||
;;
|
||||
*)
|
||||
# Failed to stop
|
||||
log_end_msg 1
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
*)
|
||||
echo "Usage: $SCRIPTNAME {start|stop|status|restart|force-reload}" >&2
|
||||
exit 3
|
||||
;;
|
||||
esac
|
||||
|
||||
:
|
63
rattail_fabric2/deploy/collectd/check-mountpoints.py
Normal file
63
rattail_fabric2/deploy/collectd/check-mountpoints.py
Normal file
|
@ -0,0 +1,63 @@
|
|||
# -*- coding: utf-8; -*-
|
||||
|
||||
import argparse
|
||||
import os
|
||||
import socket
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
|
||||
def check_mounts(mountpoints):
|
||||
for path in mountpoints:
|
||||
sys.stdout.write("{} is {}mounted\n".format(
|
||||
path, '' if is_mounted(path) else 'NOT '))
|
||||
|
||||
|
||||
def check_mounts_collectd(mountpoints):
|
||||
|
||||
hostname = os.environ.get('COLLECTD_HOSTNAME')
|
||||
if not hostname:
|
||||
hostname = socket.getfqdn()
|
||||
|
||||
plugin = 'mountpoints'
|
||||
|
||||
interval = os.environ.get('COLLECTD_INTERVAL')
|
||||
if interval:
|
||||
interval = ' interval={}'.format(interval)
|
||||
else:
|
||||
interval = ''
|
||||
|
||||
for path in mountpoints:
|
||||
name = path.strip('/').replace('/', '-')
|
||||
plugin_instance = '{}-{}'.format(plugin, name)
|
||||
|
||||
data_type = 'gauge-state'
|
||||
|
||||
value = 1 if is_mounted(path) else 0
|
||||
value = 'N:{}'.format(value)
|
||||
|
||||
msg = 'PUTVAL {}/{}/{}{} {}\n'.format(
|
||||
hostname,
|
||||
plugin_instance,
|
||||
data_type,
|
||||
interval,
|
||||
value)
|
||||
|
||||
sys.stdout.write(msg)
|
||||
|
||||
|
||||
def is_mounted(path):
|
||||
cmd = "mount | grep '{}' || true".format(path)
|
||||
output = subprocess.check_output(cmd, shell=True)
|
||||
return bool(output)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument('--collectd', action='store_true')
|
||||
parser.add_argument('mountpoints', nargs='+', metavar='MOUNTPOINT')
|
||||
args = parser.parse_args()
|
||||
if args.collectd:
|
||||
check_mounts_collectd(args.mountpoints)
|
||||
else:
|
||||
check_mounts(args.mountpoints)
|
13
rattail_fabric2/deploy/corepos/fannie-config.php.mako
Normal file
13
rattail_fabric2/deploy/corepos/fannie-config.php.mako
Normal file
|
@ -0,0 +1,13 @@
|
|||
<?php
|
||||
|
||||
$FANNIE_ROOT = '${rootdir}/IS4C/fannie/';
|
||||
$FANNIE_URL = '${rooturl}/';
|
||||
$FANNIE_SERVER = '127.0.0.1';
|
||||
$FANNIE_SERVER_DBMS = 'MYSQLI';
|
||||
$FANNIE_SERVER_USER = '${mysql_username}';
|
||||
$FANNIE_SERVER_PW = '${mysql_password}';
|
||||
$FANNIE_OP_DB = '${mysql_name_prefix}core_op';
|
||||
$FANNIE_TRANS_DB = '${mysql_name_prefix}core_trans';
|
||||
$FANNIE_ARCHIVE_DB = '${mysql_name_prefix}trans_archive';
|
||||
|
||||
?>
|
|
@ -1,148 +0,0 @@
|
|||
#!/bin/sh
|
||||
################################################################################
|
||||
#
|
||||
# Rattail -- Retail Software Framework
|
||||
# Copyright © 2010-2018 Lance Edgar
|
||||
#
|
||||
# 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/>.
|
||||
#
|
||||
################################################################################
|
||||
#
|
||||
# Generic 'init' script for all Rattail daemons. Originally based on the
|
||||
# skeleton file from a Debian 6 system, but tweaked plenty since...
|
||||
#
|
||||
# NOTE: The "calling" script *must* define $NAME and $DESC (at least).
|
||||
#
|
||||
################################################################################
|
||||
|
||||
# Files in /etc/default and/or the "calling" init script may define any of the
|
||||
# following as necessary, to override:
|
||||
|
||||
SCRIPTNAME=${SCRIPTNAME:-"/etc/init.d/$NAME"}
|
||||
PIDFILE=${PIDFILE:-"/var/run/rattail/$NAME.pid"}
|
||||
|
||||
PYTHON=${PYTHON:-"/usr/bin/python"}
|
||||
RATTAIL=${RATTAIL:-"/usr/local/bin/rattail"}
|
||||
RATTAIL_ARGS=${RATTAIL_ARGS:-""}
|
||||
RATTAIL_SUBCMD=${RATTAIL_SUBCMD:-"${NAME##*-}"}
|
||||
RATTAIL_SUBCMD_ARGS=${RATTAIL_SUBCMD_ARGS:-""}
|
||||
|
||||
USER=${USER:-"rattail"}
|
||||
GROUP=${GROUP:-"rattail"}
|
||||
|
||||
|
||||
# Read configuration variable files if present.
|
||||
[ -r /etc/default/rattail ] && . /etc/default/rattail
|
||||
[ -r /etc/default/$NAME ] && . /etc/default/$NAME
|
||||
|
||||
# Load the VERBOSE setting and other rcS variables.
|
||||
. /lib/init/vars.sh
|
||||
|
||||
# Define LSB log_* functions.
|
||||
# Depend on lsb-base (>= 3.2-14) to ensure that this file is present
|
||||
# and status_of_proc is working.
|
||||
. /lib/lsb/init-functions
|
||||
|
||||
|
||||
create_pid_dir() {
|
||||
PIDDIR=`dirname "$PIDFILE"`
|
||||
if [ ! -d "$PIDDIR" ]; then
|
||||
mkdir --parents "$PIDDIR"
|
||||
fi
|
||||
chown $USER:$GROUP "$PIDDIR"
|
||||
}
|
||||
|
||||
|
||||
#
|
||||
# Function that starts the daemon/service
|
||||
#
|
||||
do_start()
|
||||
{
|
||||
# Return
|
||||
# 0 if daemon has been started
|
||||
# 1 if daemon was already running
|
||||
# 2 if daemon could not be started
|
||||
create_pid_dir
|
||||
start-stop-daemon --start --pidfile "$PIDFILE" --exec "$PYTHON" --user $USER --test --quiet >/dev/null \
|
||||
|| return 1
|
||||
start-stop-daemon --start --pidfile "$PIDFILE" --exec "$PYTHON" --startas "$RATTAIL" --chuid $USER --group $GROUP --quiet -- \
|
||||
$RATTAIL_ARGS $RATTAIL_SUBCMD --pidfile="$PIDFILE" $RATTAIL_SUBCMD_ARGS start \
|
||||
|| return 2
|
||||
}
|
||||
|
||||
|
||||
#
|
||||
# Function that stops the daemon/service
|
||||
#
|
||||
do_stop()
|
||||
{
|
||||
# Return
|
||||
# 0 if daemon has been stopped
|
||||
# 1 if daemon was already stopped
|
||||
# 2 if daemon could not be stopped
|
||||
# other if a failure occurred
|
||||
/usr/local/bin/check-rattail-daemon "$NAME" >/dev/null \
|
||||
|| return 1
|
||||
sudo -u $USER "$RATTAIL" $RATTAIL_ARGS $RATTAIL_SUBCMD --pidfile="$PIDFILE" $RATTAIL_SUBCMD_ARGS stop >/dev/null 2>&1 \
|
||||
|| return 2
|
||||
}
|
||||
|
||||
|
||||
case "$1" in
|
||||
start)
|
||||
[ "$VERBOSE" != no ] && log_daemon_msg "Starting $DESC" "$NAME"
|
||||
do_start
|
||||
case "$?" in
|
||||
0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;;
|
||||
2) [ "$VERBOSE" != no ] && log_end_msg 1 ;;
|
||||
esac
|
||||
;;
|
||||
stop)
|
||||
[ "$VERBOSE" != no ] && log_daemon_msg "Stopping $DESC" "$NAME"
|
||||
do_stop
|
||||
case "$?" in
|
||||
0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;;
|
||||
2) [ "$VERBOSE" != no ] && log_end_msg 1 ;;
|
||||
esac
|
||||
;;
|
||||
status)
|
||||
/usr/local/bin/check-rattail-daemon "$NAME" && exit 0 || exit $?
|
||||
;;
|
||||
restart|force-reload)
|
||||
log_daemon_msg "Restarting $DESC" "$NAME"
|
||||
do_stop
|
||||
case "$?" in
|
||||
0|1)
|
||||
do_start
|
||||
case "$?" in
|
||||
0) log_end_msg 0 ;;
|
||||
1) log_end_msg 1 ;; # Old process is still running
|
||||
*) log_end_msg 1 ;; # Failed to start
|
||||
esac
|
||||
;;
|
||||
*)
|
||||
# Failed to stop
|
||||
log_end_msg 1
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
*)
|
||||
echo "Usage: $SCRIPTNAME {start|stop|status|restart|force-reload}" >&2
|
||||
exit 3
|
||||
;;
|
||||
esac
|
||||
|
||||
:
|
|
@ -1,142 +0,0 @@
|
|||
#!/bin/sh
|
||||
# -*- coding: utf-8; -*-
|
||||
################################################################################
|
||||
#
|
||||
# Rattail -- Retail Software Framework
|
||||
# Copyright © 2010-2018 Lance Edgar
|
||||
#
|
||||
# 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/>.
|
||||
#
|
||||
################################################################################
|
||||
|
||||
|
||||
# This script is mostly based on the ``/etc/init.d/skeleton`` file from a
|
||||
# Debian 6 system.
|
||||
|
||||
|
||||
DESC=${DESC:-"Rattail Data Synchronizer"}
|
||||
NAME=${NAME:-"rattail-datasync"}
|
||||
SCRIPTNAME=${SCRIPTNAME:-"/etc/init.d/$NAME"}
|
||||
PIDFILE=${PIDFILE:-"/var/run/rattail/$NAME.pid"}
|
||||
|
||||
PYTHON=${PYTHON:-"/usr/bin/python"}
|
||||
RATTAIL=${RATTAIL:-"/usr/local/bin/rattail"}
|
||||
RATTAIL_ARGS=${RATTAIL_ARGS:-""}
|
||||
DATASYNC_ARGS=${DATASYNC_ARGS:-"--pidfile=$PIDFILE"}
|
||||
|
||||
USER=${USER:-"rattail"}
|
||||
GROUP=${GROUP:-"rattail"}
|
||||
|
||||
|
||||
# Read configuration variable files if present.
|
||||
[ -r /etc/default/rattail ] && . /etc/default/rattail
|
||||
[ -r /etc/default/$NAME ] && . /etc/default/$NAME
|
||||
|
||||
# Load the VERBOSE setting and other rcS variables.
|
||||
. /lib/init/vars.sh
|
||||
|
||||
# Define LSB log_* functions.
|
||||
# Depend on lsb-base (>= 3.2-14) to ensure that this file is present
|
||||
# and status_of_proc is working.
|
||||
. /lib/lsb/init-functions
|
||||
|
||||
|
||||
create_pid_dir() {
|
||||
PIDDIR=`dirname "$PIDFILE"`
|
||||
if [ ! -d "$PIDDIR" ]; then
|
||||
mkdir --parents "$PIDDIR"
|
||||
fi
|
||||
chown $USER:$GROUP "$PIDDIR"
|
||||
}
|
||||
|
||||
|
||||
#
|
||||
# Function that starts the daemon/service
|
||||
#
|
||||
do_start()
|
||||
{
|
||||
# Return
|
||||
# 0 if daemon has been started
|
||||
# 1 if daemon was already running
|
||||
# 2 if daemon could not be started
|
||||
create_pid_dir
|
||||
start-stop-daemon --start --pidfile $PIDFILE --exec $PYTHON --user $USER --test --quiet > /dev/null \
|
||||
|| return 1
|
||||
start-stop-daemon --start --pidfile $PIDFILE --exec $PYTHON --startas $RATTAIL --chuid $USER --group $GROUP --quiet -- \
|
||||
$RATTAIL_ARGS datasync $DATASYNC_ARGS start \
|
||||
|| return 2
|
||||
}
|
||||
|
||||
|
||||
#
|
||||
# Function that stops the daemon/service
|
||||
#
|
||||
do_stop()
|
||||
{
|
||||
# Return
|
||||
# 0 if daemon has been stopped
|
||||
# 1 if daemon was already stopped
|
||||
# 2 if daemon could not be stopped
|
||||
# other if a failure occurred
|
||||
sudo -u $USER $RATTAIL $RATTAIL_ARGS datasync $DATASYNC_ARGS stop > /dev/null 2>&1
|
||||
}
|
||||
|
||||
|
||||
case "$1" in
|
||||
start)
|
||||
[ "$VERBOSE" != no ] && log_daemon_msg "Starting $DESC" "$NAME"
|
||||
do_start
|
||||
case "$?" in
|
||||
0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;;
|
||||
2) [ "$VERBOSE" != no ] && log_end_msg 1 ;;
|
||||
esac
|
||||
;;
|
||||
stop)
|
||||
[ "$VERBOSE" != no ] && log_daemon_msg "Stopping $DESC" "$NAME"
|
||||
do_stop
|
||||
case "$?" in
|
||||
0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;;
|
||||
2) [ "$VERBOSE" != no ] && log_end_msg 1 ;;
|
||||
esac
|
||||
;;
|
||||
status)
|
||||
status_of_proc "$DAEMON" "$NAME" && exit 0 || exit $?
|
||||
;;
|
||||
restart|force-reload)
|
||||
log_daemon_msg "Restarting $DESC" "$NAME"
|
||||
do_stop
|
||||
case "$?" in
|
||||
0|1)
|
||||
do_start
|
||||
case "$?" in
|
||||
0) log_end_msg 0 ;;
|
||||
1) log_end_msg 1 ;; # Old process is still running
|
||||
*) log_end_msg 1 ;; # Failed to start
|
||||
esac
|
||||
;;
|
||||
*)
|
||||
# Failed to stop
|
||||
log_end_msg 1
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
*)
|
||||
echo "Usage: $SCRIPTNAME {start|stop|status|restart|force-reload}" >&2
|
||||
exit 3
|
||||
;;
|
||||
esac
|
||||
|
||||
:
|
|
@ -1,142 +0,0 @@
|
|||
#!/bin/sh
|
||||
# -*- coding: utf-8; -*-
|
||||
################################################################################
|
||||
#
|
||||
# Rattail -- Retail Software Framework
|
||||
# Copyright © 2010-2018 Lance Edgar
|
||||
#
|
||||
# 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/>.
|
||||
#
|
||||
################################################################################
|
||||
|
||||
|
||||
# This script is mostly based on the ``/etc/init.d/skeleton`` file from a
|
||||
# Debian 6 system.
|
||||
|
||||
|
||||
DESC=${DESC:-"Rattail File Monitor"}
|
||||
NAME=${NAME:-"rattail-filemon"}
|
||||
SCRIPTNAME=${SCRIPTNAME:-"/etc/init.d/$NAME"}
|
||||
PIDFILE=${PIDFILE:-"/var/run/rattail/$NAME.pid"}
|
||||
|
||||
PYTHON=${PYTHON:-"/usr/bin/python"}
|
||||
RATTAIL=${RATTAIL:-"/usr/local/bin/rattail"}
|
||||
RATTAIL_ARGS=${RATTAIL_ARGS:-""}
|
||||
FILEMON_ARGS=${FILEMON_ARGS:-"--pidfile=$PIDFILE"}
|
||||
|
||||
USER=${USER:-"rattail"}
|
||||
GROUP=${GROUP:-"rattail"}
|
||||
|
||||
|
||||
# Read configuration variable files if present.
|
||||
[ -r /etc/default/rattail ] && . /etc/default/rattail
|
||||
[ -r /etc/default/$NAME ] && . /etc/default/$NAME
|
||||
|
||||
# Load the VERBOSE setting and other rcS variables.
|
||||
. /lib/init/vars.sh
|
||||
|
||||
# Define LSB log_* functions.
|
||||
# Depend on lsb-base (>= 3.2-14) to ensure that this file is present
|
||||
# and status_of_proc is working.
|
||||
. /lib/lsb/init-functions
|
||||
|
||||
|
||||
create_pid_dir() {
|
||||
PIDDIR=`dirname "$PIDFILE"`
|
||||
if [ ! -d "$PIDDIR" ]; then
|
||||
mkdir --parents "$PIDDIR"
|
||||
fi
|
||||
chown $USER:$GROUP "$PIDDIR"
|
||||
}
|
||||
|
||||
|
||||
#
|
||||
# Function that starts the daemon/service
|
||||
#
|
||||
do_start()
|
||||
{
|
||||
# Return
|
||||
# 0 if daemon has been started
|
||||
# 1 if daemon was already running
|
||||
# 2 if daemon could not be started
|
||||
create_pid_dir
|
||||
start-stop-daemon --start --pidfile $PIDFILE --exec $PYTHON --user $USER --test --quiet > /dev/null \
|
||||
|| return 1
|
||||
start-stop-daemon --start --pidfile $PIDFILE --exec $PYTHON --startas $RATTAIL --chuid $USER --group $GROUP --quiet -- \
|
||||
$RATTAIL_ARGS filemon $FILEMON_ARGS start \
|
||||
|| return 2
|
||||
}
|
||||
|
||||
|
||||
#
|
||||
# Function that stops the daemon/service
|
||||
#
|
||||
do_stop()
|
||||
{
|
||||
# Return
|
||||
# 0 if daemon has been stopped
|
||||
# 1 if daemon was already stopped
|
||||
# 2 if daemon could not be stopped
|
||||
# other if a failure occurred
|
||||
sudo -u $USER $RATTAIL $RATTAIL_ARGS filemon $FILEMON_ARGS stop > /dev/null 2>&1
|
||||
}
|
||||
|
||||
|
||||
case "$1" in
|
||||
start)
|
||||
[ "$VERBOSE" != no ] && log_daemon_msg "Starting $DESC" "$NAME"
|
||||
do_start
|
||||
case "$?" in
|
||||
0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;;
|
||||
2) [ "$VERBOSE" != no ] && log_end_msg 1 ;;
|
||||
esac
|
||||
;;
|
||||
stop)
|
||||
[ "$VERBOSE" != no ] && log_daemon_msg "Stopping $DESC" "$NAME"
|
||||
do_stop
|
||||
case "$?" in
|
||||
0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;;
|
||||
2) [ "$VERBOSE" != no ] && log_end_msg 1 ;;
|
||||
esac
|
||||
;;
|
||||
status)
|
||||
status_of_proc "$DAEMON" "$NAME" && exit 0 || exit $?
|
||||
;;
|
||||
restart|force-reload)
|
||||
log_daemon_msg "Restarting $DESC" "$NAME"
|
||||
do_stop
|
||||
case "$?" in
|
||||
0|1)
|
||||
do_start
|
||||
case "$?" in
|
||||
0) log_end_msg 0 ;;
|
||||
1) log_end_msg 1 ;; # Old process is still running
|
||||
*) log_end_msg 1 ;; # Failed to start
|
||||
esac
|
||||
;;
|
||||
*)
|
||||
# Failed to stop
|
||||
log_end_msg 1
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
*)
|
||||
echo "Usage: $SCRIPTNAME {start|stop|status|restart|force-reload}" >&2
|
||||
exit 3
|
||||
;;
|
||||
esac
|
||||
|
||||
:
|
14
rattail_fabric2/deploy/luigi/cron-overnight.sh.mako
Executable file
14
rattail_fabric2/deploy/luigi/cron-overnight.sh.mako
Executable file
|
@ -0,0 +1,14 @@
|
|||
#!/bin/sh -e
|
||||
|
||||
cd ${envroot}
|
||||
|
||||
export RATTAIL_CONFIG_FILES=${overnight_conf}
|
||||
# nb. avoid rich-formatted traceback here since it's so "noisy"
|
||||
export _TYPER_STANDARD_TRACEBACK=1
|
||||
|
||||
bin/rattail --no-versioning overnight -k ${automation.lower()} <%text>\</%text>
|
||||
% if email_key is not Undefined and email_key:
|
||||
--email-key '${email_key}' <%text>\</%text>
|
||||
% endif
|
||||
--no-email-if-empty <%text>\</%text>
|
||||
--wait
|
8
rattail_fabric2/deploy/luigi/crontab.mako
Normal file
8
rattail_fabric2/deploy/luigi/crontab.mako
Normal file
|
@ -0,0 +1,8 @@
|
|||
## -*- mode: conf; -*-
|
||||
|
||||
% if mailto:
|
||||
MAILTO="${','.join(mailto)}"
|
||||
% endif
|
||||
|
||||
# rotate logs and restart Luigi at *just before* 12:00am midnight
|
||||
55 23 * * * root ${appdir}/rotate-luigi-logs.sh
|
39
rattail_fabric2/deploy/luigi/logging.conf.mako
Normal file
39
rattail_fabric2/deploy/luigi/logging.conf.mako
Normal file
|
@ -0,0 +1,39 @@
|
|||
## -*- mode: conf; -*-
|
||||
|
||||
<%text>############################################################</%text>
|
||||
#
|
||||
# Luigi logging config
|
||||
#
|
||||
<%text>############################################################</%text>
|
||||
|
||||
|
||||
[loggers]
|
||||
keys = root
|
||||
|
||||
[handlers]
|
||||
keys = file, console
|
||||
|
||||
[formatters]
|
||||
keys = generic, console
|
||||
|
||||
[logger_root]
|
||||
handlers = file, console
|
||||
level = DEBUG
|
||||
|
||||
[handler_file]
|
||||
class = handlers.WatchedFileHandler
|
||||
args = ('${appdir}/luigi/log/luigi.log', 'a', 'utf_8')
|
||||
formatter = generic
|
||||
|
||||
[handler_console]
|
||||
class = StreamHandler
|
||||
args = (sys.stderr,)
|
||||
formatter = console
|
||||
level = WARNING
|
||||
|
||||
[formatter_generic]
|
||||
format = %(asctime)s %(levelname)-5.5s [%(name)s][%(threadName)s] %(message)s
|
||||
datefmt = %Y-%m-%d %H:%M:%S
|
||||
|
||||
[formatter_console]
|
||||
format = %(levelname)-5.5s [%(name)s][%(threadName)s] %(message)s
|
24
rattail_fabric2/deploy/luigi/luigi-logrotate.conf.mako
Normal file
24
rattail_fabric2/deploy/luigi/luigi-logrotate.conf.mako
Normal file
|
@ -0,0 +1,24 @@
|
|||
## -*- mode: conf; -*-
|
||||
|
||||
${appdir}/luigi/log/luigi.log {
|
||||
daily
|
||||
missingok
|
||||
rotate 30
|
||||
compress
|
||||
delaycompress
|
||||
notifempty
|
||||
create 600 rattail rattail
|
||||
}
|
||||
|
||||
${appdir}/luigi/log/luigi-server.log {
|
||||
daily
|
||||
missingok
|
||||
rotate 30
|
||||
compress
|
||||
delaycompress
|
||||
notifempty
|
||||
create 600 rattail rattail
|
||||
postrotate
|
||||
supervisorctl restart luigi:luigid > /dev/null
|
||||
endscript
|
||||
}
|
52
rattail_fabric2/deploy/luigi/luigi.cfg.mako
Normal file
52
rattail_fabric2/deploy/luigi/luigi.cfg.mako
Normal file
|
@ -0,0 +1,52 @@
|
|||
## -*- mode: conf; -*-
|
||||
|
||||
<%text>############################################################</%text>
|
||||
#
|
||||
# Luigi config
|
||||
#
|
||||
# cf. https://luigi.readthedocs.io/en/stable/configuration.html
|
||||
#
|
||||
<%text>############################################################</%text>
|
||||
|
||||
|
||||
[core]
|
||||
logging_conf_file = ${appdir}/luigi/logging.conf
|
||||
|
||||
% if LUIGI2:
|
||||
|
||||
# Number of seconds to wait before timing out when making an API call. Defaults
|
||||
# to 10.0
|
||||
# (sometimes things can lag for us and we simply need to give it more time)
|
||||
rpc_connect_timeout = 60
|
||||
|
||||
# The maximum number of retries to connect the central scheduler before giving
|
||||
# up. Defaults to 3
|
||||
# (occasional network issues seem to cause us to need more/longer retries)
|
||||
rpc_retry_attempts = 10
|
||||
|
||||
# Number of seconds to wait before the next attempt will be started to connect
|
||||
# to the central scheduler between two retry attempts. Defaults to 30
|
||||
# (occasional network issues seem to cause us to need more/longer retries)
|
||||
rpc_retry_wait = 60
|
||||
|
||||
% endif
|
||||
|
||||
[retcode]
|
||||
# cf. https://luigi.readthedocs.io/en/stable/configuration.html#retcode-config
|
||||
# The following return codes are the recommended exit codes for Luigi
|
||||
# They are in increasing level of severity (for most applications)
|
||||
already_running=10
|
||||
missing_data=20
|
||||
not_run=25
|
||||
task_failed=30
|
||||
scheduling_error=35
|
||||
unhandled_exception=40
|
||||
|
||||
[scheduler]
|
||||
state_path = ${appdir}/luigi/state.pickle
|
||||
% if db_connection:
|
||||
record_task_history = true
|
||||
|
||||
[task_history]
|
||||
db_connection = ${db_connection}
|
||||
% endif
|
28
rattail_fabric2/deploy/luigi/overnight.sh.mako
Executable file
28
rattail_fabric2/deploy/luigi/overnight.sh.mako
Executable file
|
@ -0,0 +1,28 @@
|
|||
#!/bin/bash
|
||||
<%text>############################################################</%text>
|
||||
#
|
||||
# overnight automation for '${automation}'
|
||||
#
|
||||
<%text>############################################################</%text>
|
||||
|
||||
set -e
|
||||
|
||||
DATE=$1
|
||||
|
||||
if [ "$1" = "--verbose" ]; then
|
||||
DATE=$2
|
||||
VERBOSE='--verbose'
|
||||
else
|
||||
VERBOSE=
|
||||
fi
|
||||
|
||||
if [ "$DATE" = "" ]; then
|
||||
DATE=`date --date='yesterday' +%Y-%m-%d`
|
||||
fi
|
||||
|
||||
LUIGI='${envroot}/bin/luigi --logging-conf-file logging.conf'
|
||||
export PYTHONPATH=${appdir}
|
||||
|
||||
cd ${appdir}/luigi
|
||||
|
||||
$LUIGI --module luigitasks.overnight_${automation.lower()} Overnight${automation} --date $DATE
|
14
rattail_fabric2/deploy/luigi/restart-overnight.sh.mako
Normal file
14
rattail_fabric2/deploy/luigi/restart-overnight.sh.mako
Normal file
|
@ -0,0 +1,14 @@
|
|||
#!/bin/sh -e
|
||||
|
||||
cd ${envroot}
|
||||
|
||||
export RATTAIL_CONFIG_FILES=${overnight_conf}
|
||||
# nb. avoid rich-formatted traceback here since it's so "noisy"
|
||||
export _TYPER_STANDARD_TRACEBACK=1
|
||||
|
||||
bin/rattail --no-versioning overnight -k ${automation.lower()} <%text>\</%text>
|
||||
% if email_key is not Undefined and email_key:
|
||||
--email-key '${email_key}' <%text>\</%text>
|
||||
% endif
|
||||
--email-if-empty <%text>\</%text>
|
||||
--no-wait
|
14
rattail_fabric2/deploy/luigi/rotate-logs.sh.mako
Executable file
14
rattail_fabric2/deploy/luigi/rotate-logs.sh.mako
Executable file
|
@ -0,0 +1,14 @@
|
|||
#!/bin/sh -e
|
||||
<%text>######################################################################</%text>
|
||||
#
|
||||
# rotate Luigi server log file
|
||||
#
|
||||
<%text>######################################################################</%text>
|
||||
|
||||
if [ "$1" = "--verbose" ]; then
|
||||
VERBOSE='--verbose'
|
||||
else
|
||||
VERBOSE=
|
||||
fi
|
||||
|
||||
/usr/sbin/logrotate $VERBOSE ${appdir}/luigi/logrotate.conf
|
11
rattail_fabric2/deploy/luigi/supervisor.conf.mako
Normal file
11
rattail_fabric2/deploy/luigi/supervisor.conf.mako
Normal file
|
@ -0,0 +1,11 @@
|
|||
## -*- mode: conf; -*-
|
||||
|
||||
[group:luigi]
|
||||
programs=luigid
|
||||
|
||||
[program:luigid]
|
||||
command=${envroot}/bin/luigid --logdir ${appdir}/luigi/log --state-path ${appdir}/luigi/state.pickle --address 127.0.0.1
|
||||
directory=${appdir}/work
|
||||
environment=LUIGI_CONFIG_PATH="${appdir}/luigi/luigi.cfg"
|
||||
user=${user}
|
||||
autostart=${'true' if autostart else 'false'}
|
|
@ -1,127 +0,0 @@
|
|||
#!/bin/sh
|
||||
# -*- coding: utf-8; -*-
|
||||
################################################################################
|
||||
#
|
||||
# Rattail -- Retail Software Framework
|
||||
# Copyright © 2010-2018 Lance Edgar
|
||||
#
|
||||
# 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/>.
|
||||
#
|
||||
################################################################################
|
||||
#
|
||||
# Generic 'init' script for Luigi central scheduler (luigid)
|
||||
#
|
||||
################################################################################
|
||||
|
||||
# "calling" init script may define any of the following as necessary:
|
||||
|
||||
NAME=${NAME:-"rattail-luigid"}
|
||||
SCRIPTNAME=${SCRIPTNAME:-"/etc/init.d/$NAME"}
|
||||
DESC=${DESC:-"Luigi Scheduler for Rattail"}
|
||||
DAEMON=${DAEMON:-"/usr/local/bin/luigid"}
|
||||
PYTHON=${PYTHON:-"/usr/local/bin/python"}
|
||||
USER=${USER:-"rattail"}
|
||||
GROUP=${GROUP:-"rattail"}
|
||||
PIDFILE=${PIDFILE:-"/var/run/rattail/$NAME.pid"}
|
||||
LOGDIR=${LOGDIR:-"/var/log/rattail/$NAME"}
|
||||
STATEFILE=${STATEFILE:-"/var/run/rattail/$NAME.pickle"}
|
||||
ADDRESS=${ADDRESS:-"0.0.0.0"}
|
||||
|
||||
|
||||
create_pid_dir() {
|
||||
PIDDIR=`dirname "$PIDFILE"`
|
||||
if [ ! -d "$PIDDIR" ]; then
|
||||
mkdir --parents "$PIDDIR"
|
||||
fi
|
||||
chown $USER:$GROUP "$PIDDIR"
|
||||
}
|
||||
|
||||
|
||||
# Return
|
||||
# 0 if daemon has been started
|
||||
# 1 if daemon was already running
|
||||
# 2 if daemon could not be started
|
||||
do_start() {
|
||||
|
||||
create_pid_dir
|
||||
|
||||
start-stop-daemon --test --start --exec $DAEMON --user $USER --pidfile $PIDFILE --quiet > /dev/null \
|
||||
|| return 1
|
||||
start-stop-daemon --start --chuid $USER --exec $DAEMON --pidfile $PIDFILE --background --quiet -- \
|
||||
--background --pidfile $PIDFILE --logdir $LOGDIR --state-path $STATEFILE --address $ADDRESS \
|
||||
|| return 2
|
||||
}
|
||||
|
||||
|
||||
# Return
|
||||
# 0 if daemon has been stopped
|
||||
# 1 if daemon was already stopped
|
||||
# 2 if daemon could not be stopped
|
||||
# other if a failure occurred
|
||||
do_stop()
|
||||
{
|
||||
start-stop-daemon --test --stop --exec $PYTHON --user $USER --pidfile $PIDFILE --quiet > /dev/null \
|
||||
|| return 1
|
||||
start-stop-daemon --stop --exec $PYTHON --user $USER --pidfile $PIDFILE --quiet > /dev/null \
|
||||
|| return 2
|
||||
}
|
||||
|
||||
|
||||
case "$1" in
|
||||
start)
|
||||
[ "$VERBOSE" != no ] && log_daemon_msg "Starting $DESC" "$NAME"
|
||||
do_start
|
||||
case "$?" in
|
||||
0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;;
|
||||
2) [ "$VERBOSE" != no ] && log_end_msg 1 ;;
|
||||
esac
|
||||
;;
|
||||
stop)
|
||||
[ "$VERBOSE" != no ] && log_daemon_msg "Stopping $DESC" "$NAME"
|
||||
do_stop
|
||||
case "$?" in
|
||||
0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;;
|
||||
2) [ "$VERBOSE" != no ] && log_end_msg 1 ;;
|
||||
esac
|
||||
;;
|
||||
status)
|
||||
/usr/local/bin/check-rattail-daemon "$NAME" && exit 0 || exit $?
|
||||
;;
|
||||
restart|force-reload)
|
||||
log_daemon_msg "Restarting $DESC" "$NAME"
|
||||
do_stop
|
||||
case "$?" in
|
||||
0|1)
|
||||
do_start
|
||||
case "$?" in
|
||||
0) log_end_msg 0 ;;
|
||||
1) log_end_msg 1 ;; # Old process is still running
|
||||
*) log_end_msg 1 ;; # Failed to start
|
||||
esac
|
||||
;;
|
||||
*)
|
||||
# Failed to stop
|
||||
log_end_msg 1
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
*)
|
||||
echo "Usage: $SCRIPTNAME {start|stop|status|restart|force-reload}" >&2
|
||||
exit 3
|
||||
;;
|
||||
esac
|
||||
|
||||
:
|
33
rattail_fabric2/deploy/rattail/check-datasync-queue.mako
Executable file
33
rattail_fabric2/deploy/rattail/check-datasync-queue.mako
Executable file
|
@ -0,0 +1,33 @@
|
|||
#!${envroot}/bin/python
|
||||
|
||||
import os
|
||||
import sys
|
||||
import argparse
|
||||
import subprocess
|
||||
|
||||
|
||||
def check_datasync_queue(timeout):
|
||||
os.chdir(sys.prefix)
|
||||
|
||||
retcode = subprocess.call([
|
||||
'bin/rattail',
|
||||
'-c', 'app/${config}.conf',
|
||||
'--no-versioning',
|
||||
'datasync',
|
||||
'--timeout', timeout,
|
||||
'check',
|
||||
])
|
||||
|
||||
if retcode == 1:
|
||||
sys.exit(2)
|
||||
|
||||
elif retcode:
|
||||
print("unknown issue")
|
||||
sys.exit(3)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument('timeout')
|
||||
args = parser.parse_args()
|
||||
check_datasync_queue(args.timeout)
|
34
rattail_fabric2/deploy/rattail/check-datasync-watchers.mako
Executable file
34
rattail_fabric2/deploy/rattail/check-datasync-watchers.mako
Executable file
|
@ -0,0 +1,34 @@
|
|||
#!${envroot}/bin/python
|
||||
|
||||
import os
|
||||
import sys
|
||||
import argparse
|
||||
import subprocess
|
||||
|
||||
|
||||
def check_datasync_queue(timeout):
|
||||
os.chdir(sys.prefix)
|
||||
|
||||
retcode = subprocess.call([
|
||||
'bin/rattail',
|
||||
'-c', 'app/datasync.conf',
|
||||
'-c', 'app/${config}.conf',
|
||||
'--no-versioning',
|
||||
'datasync',
|
||||
'--timeout', timeout,
|
||||
'check-watchers',
|
||||
])
|
||||
|
||||
if retcode == 1:
|
||||
sys.exit(2)
|
||||
|
||||
elif retcode:
|
||||
print("unknown issue")
|
||||
sys.exit(3)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument('timeout')
|
||||
args = parser.parse_args()
|
||||
check_datasync_queue(args.timeout)
|
|
@ -19,9 +19,8 @@ configure_logging = true
|
|||
|
||||
[rattail.mail]
|
||||
smtp.server = localhost
|
||||
templates = rattail:templates/mail
|
||||
default.from = root
|
||||
default.to = root
|
||||
default.from = ${getattr(env, 'email_default_sender', 'rattail@localhost')}
|
||||
default.to = ${', '.join(getattr(env, 'email_default_recipients', ['root@localhost']))}
|
||||
|
||||
|
||||
${'#'}#############################
|
||||
|
@ -88,7 +87,7 @@ formatter = console
|
|||
|
||||
[handler_email]
|
||||
class = handlers.SMTPHandler
|
||||
args = ('localhost', 'root', ['root'], "[Rattail] Logging")
|
||||
args = ('localhost', '${getattr(env, 'email_default_sender', 'rattail@localhost')}', ${getattr(env, 'email_default_recipients', ['root@localhost'])}, "[Rattail] Logging")
|
||||
formatter = generic
|
||||
level = ERROR
|
||||
|
||||
|
|
61
rattail_fabric2/docker.py
Normal file
61
rattail_fabric2/docker.py
Normal file
|
@ -0,0 +1,61 @@
|
|||
# -*- coding: utf-8; -*-
|
||||
################################################################################
|
||||
#
|
||||
# Rattail -- Retail Software Framework
|
||||
# Copyright © 2010-2023 Lance Edgar
|
||||
#
|
||||
# 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 Docker
|
||||
"""
|
||||
|
||||
from rattail_fabric2 import apt, exists, mkdir
|
||||
|
||||
|
||||
def setup_repository(c, flavor='debian'):
|
||||
"""
|
||||
Setup the APT repository for Docker
|
||||
|
||||
https://docs.docker.com/engine/install/debian/#install-using-the-repository
|
||||
https://docs.docker.com/engine/install/ubuntu/#install-using-the-repository
|
||||
"""
|
||||
apt.install(c, 'ca-certificates',
|
||||
'curl',
|
||||
'gnupg',
|
||||
'lsb-release')
|
||||
|
||||
mkdir(c, '/etc/apt/keyrings', use_sudo=True)
|
||||
|
||||
if not exists(c, '/etc/apt/keyrings/docker.gpg'):
|
||||
c.sudo("bash -c 'curl -fsSL https://download.docker.com/linux/{}/gpg | gpg --dearmor -o /etc/apt/keyrings/docker.gpg'".format(flavor))
|
||||
|
||||
c.sudo("""bash -c 'echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/{} $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null'""".format(flavor))
|
||||
|
||||
apt.update(c)
|
||||
|
||||
|
||||
def install_engine(c):
|
||||
"""
|
||||
Install the Docker engine
|
||||
|
||||
https://docs.docker.com/engine/install/debian/#install-docker-engine
|
||||
"""
|
||||
apt.install(c, 'docker-ce',
|
||||
'docker-ce-cli',
|
||||
'containerd.io',
|
||||
'docker-compose-plugin')
|
38
rattail_fabric2/ejabberd.py
Normal file
38
rattail_fabric2/ejabberd.py
Normal file
|
@ -0,0 +1,38 @@
|
|||
# -*- coding: utf-8; -*-
|
||||
################################################################################
|
||||
#
|
||||
# Rattail -- Retail Software Framework
|
||||
# Copyright © 2010-2020 Lance Edgar
|
||||
#
|
||||
# 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 ejabberd XMPP server
|
||||
"""
|
||||
|
||||
import re
|
||||
|
||||
|
||||
def get_node_name(c):
|
||||
"""
|
||||
Returns the ejabberd node name for the current server.
|
||||
"""
|
||||
result = c.sudo('ejabberdctl status')
|
||||
match = re.match(r'^The node (\S+@\S+) is started with status: started',
|
||||
result.stdout)
|
||||
if match:
|
||||
return match.group(1)
|
|
@ -32,7 +32,7 @@ def install_from_source(c, user='rattail', branch=None):
|
|||
Install the FreeTDS library from source.
|
||||
|
||||
Per instructions found here:
|
||||
https://github.com/FreeTDS/freetds/blob/master/INSTALL.GIT
|
||||
https://github.com/FreeTDS/freetds/blob/master/INSTALL.md
|
||||
"""
|
||||
apt.install(
|
||||
c,
|
||||
|
|
175
rattail_fabric2/luigi.py
Normal file
175
rattail_fabric2/luigi.py
Normal file
|
@ -0,0 +1,175 @@
|
|||
# -*- coding: utf-8; -*-
|
||||
################################################################################
|
||||
#
|
||||
# Rattail -- Retail Software Framework
|
||||
# Copyright © 2010-2023 Lance Edgar
|
||||
#
|
||||
# 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 Luigi apps
|
||||
"""
|
||||
|
||||
import os
|
||||
import re
|
||||
from pkg_resources import parse_version
|
||||
|
||||
from rattail_fabric2 import postgresql, make_deploy, mkdir
|
||||
|
||||
|
||||
deploy_common = make_deploy(__file__)
|
||||
|
||||
|
||||
def install_luigi(c, envroot, luigi='luigi', user='rattail',
|
||||
db=False, dbuser='rattail', dbpass='TODO_PASSWORD',
|
||||
db_connection=None,
|
||||
supervisor=False, autostart=False,
|
||||
crontab=False, crontab_mailto=None):
|
||||
"""
|
||||
Install and configure Luigi to the given virtualenv.
|
||||
"""
|
||||
envroot = envroot.rstrip('/')
|
||||
envname = os.path.basename(envroot)
|
||||
appdir = '{}/app'.format(envroot)
|
||||
|
||||
# package
|
||||
c.sudo("""bash -lc "workon {} && pip install '{}'" """.format(envname, luigi),
|
||||
user=user)
|
||||
|
||||
# detect luigi version
|
||||
LUIGI2 = False
|
||||
output = c.sudo("bash -lc 'workon {} && pip show luigi | grep Version'".format(envname), user=user).stdout.strip()
|
||||
match = re.match(r'^Version: (\d+\S+)$', output)
|
||||
if match:
|
||||
if parse_version(match.group(1)) < parse_version('3'):
|
||||
LUIGI2 = True
|
||||
|
||||
# dirs
|
||||
mkdir(c, ['{}/luigi'.format(appdir),
|
||||
'{}/luigi/log'.format(appdir),
|
||||
'{}/luigitasks'.format(appdir),
|
||||
], use_sudo=True, owner=user)
|
||||
|
||||
# tasks
|
||||
c.sudo('touch {}/luigitasks/__init__.py'.format(appdir),
|
||||
user=user)
|
||||
|
||||
# database
|
||||
if db:
|
||||
postgresql.create_db(c, 'luigi', owner=dbuser)
|
||||
if not db_connection:
|
||||
db_connection = 'postgresql://{}:{}@localhost/luigi'.format(
|
||||
dbuser, dbpass)
|
||||
|
||||
# config
|
||||
deploy_common(c, 'luigi/luigi.cfg.mako', '{}/luigi/luigi.cfg'.format(appdir),
|
||||
use_sudo=True, owner=user, mode='0640',
|
||||
context={'appdir': appdir,
|
||||
'LUIGI2': LUIGI2,
|
||||
'db_connection': db_connection})
|
||||
deploy_common(c, 'luigi/logging.conf.mako', '{}/luigi/logging.conf'.format(appdir),
|
||||
use_sudo=True, owner=user,
|
||||
context={'appdir': appdir})
|
||||
|
||||
# supervisor
|
||||
if supervisor:
|
||||
c.sudo('supervisorctl stop luigi:', warn=True)
|
||||
deploy_common(c, 'luigi/supervisor.conf.mako',
|
||||
'/etc/supervisor/conf.d/luigi.conf',
|
||||
use_sudo=True,
|
||||
context={'envroot': envroot,
|
||||
'appdir': appdir,
|
||||
'user': user,
|
||||
'autostart': autostart})
|
||||
c.sudo('supervisorctl update')
|
||||
if autostart:
|
||||
c.sudo('supervisorctl start luigi:')
|
||||
|
||||
# logrotate
|
||||
deploy_common(c, 'luigi/luigi-logrotate.conf.mako', '{}/luigi/logrotate.conf'.format(appdir),
|
||||
use_sudo=True, owner='root:', # must be owned by root (TODO: why is that again?)
|
||||
context={'appdir': appdir})
|
||||
deploy_common(c, 'luigi/rotate-logs.sh.mako', '{}/rotate-luigi-logs.sh'.format(appdir),
|
||||
use_sudo=True, owner=user,
|
||||
context={'appdir': appdir})
|
||||
if crontab:
|
||||
deploy_common(c, 'luigi/crontab.mako', '/etc/cron.d/luigi',
|
||||
use_sudo=True, context={'appdir': appdir,
|
||||
'mailto': crontab_mailto})
|
||||
|
||||
|
||||
def install_overnight_script(c, envroot, user='rattail', automation='All',
|
||||
email_key=None,
|
||||
luigi=False,
|
||||
cron=True, cron_conf='app/cron.conf',
|
||||
restart=True, restart_conf='app/silent.conf'):
|
||||
"""
|
||||
Install an overnight automation script
|
||||
"""
|
||||
envroot = envroot.rstrip('/')
|
||||
appdir = '{}/app'.format(envroot)
|
||||
|
||||
# overnight-*.sh
|
||||
if luigi:
|
||||
filename = 'overnight-{}.sh'.format(automation.lower())
|
||||
deploy_common(c, 'luigi/overnight.sh.mako',
|
||||
'{}/{}'.format(appdir, filename),
|
||||
use_sudo=True, owner=user, mode='0755',
|
||||
context={'envroot': envroot, 'appdir': appdir,
|
||||
'automation': automation})
|
||||
|
||||
# cron-overnight-*.sh
|
||||
if cron:
|
||||
filename = 'cron-overnight-{}.sh'.format(automation.lower())
|
||||
deploy_common(c, 'luigi/cron-overnight.sh.mako',
|
||||
'{}/{}'.format(appdir, filename),
|
||||
use_sudo=True, owner=user, mode='0755',
|
||||
context={'envroot': envroot,
|
||||
'overnight_conf': cron_conf,
|
||||
'automation': automation,
|
||||
'email_key': email_key})
|
||||
|
||||
# restart-overnight-*.sh
|
||||
if restart:
|
||||
filename = 'restart-overnight-{}.sh'.format(automation.lower())
|
||||
deploy_common(c, 'luigi/restart-overnight.sh.mako',
|
||||
'{}/{}'.format(appdir, filename),
|
||||
use_sudo=True, owner=user, mode='0755',
|
||||
context={'envroot': envroot,
|
||||
'appdir': appdir,
|
||||
'overnight_conf': restart_conf,
|
||||
'automation': automation,
|
||||
'email_key': email_key})
|
||||
|
||||
|
||||
def register_with_supervisor(c, envroot, user='rattail', autostart=False):
|
||||
"""
|
||||
Register the Luigi scheduler daemon with supervisor
|
||||
"""
|
||||
envroot = envroot.rstrip('/')
|
||||
appdir = '{}/app'.format(envroot)
|
||||
|
||||
deploy_common(c, 'luigi/supervisor.conf.mako',
|
||||
'/etc/supervisor/conf.d/luigi.conf',
|
||||
use_sudo=True,
|
||||
context={'envroot': envroot,
|
||||
'appdir': appdir,
|
||||
'user': user,
|
||||
'autostart': autostart})
|
||||
c.sudo('supervisorctl update')
|
||||
if autostart:
|
||||
c.sudo('supervisorctl start luigi:')
|
38
rattail_fabric2/mariadb.py
Normal file
38
rattail_fabric2/mariadb.py
Normal file
|
@ -0,0 +1,38 @@
|
|||
# -*- coding: utf-8; -*-
|
||||
################################################################################
|
||||
#
|
||||
# Rattail -- Retail Software Framework
|
||||
# Copyright © 2010-2022 Lance Edgar
|
||||
#
|
||||
# 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 MariaDB
|
||||
"""
|
||||
|
||||
import re
|
||||
|
||||
|
||||
def get_version_string(c):
|
||||
"""
|
||||
Fetch the version of MariaDB running on the target system
|
||||
"""
|
||||
result = c.sudo('mysql --version')
|
||||
if not result.failed:
|
||||
match = re.match(r'^mysql .*?(\d+\.\d+\.\d+)-MariaDB', result.stdout)
|
||||
if match:
|
||||
return match.group(1)
|
43
rattail_fabric2/mssql.py
Normal file
43
rattail_fabric2/mssql.py
Normal file
|
@ -0,0 +1,43 @@
|
|||
# -*- coding: utf-8; -*-
|
||||
################################################################################
|
||||
#
|
||||
# Rattail -- Retail Software Framework
|
||||
# Copyright © 2010-2020 Lance Edgar
|
||||
#
|
||||
# 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 MS SQL Server
|
||||
"""
|
||||
|
||||
from rattail_fabric2 import apt
|
||||
|
||||
|
||||
def install_mssql_odbc(c, accept_eula=None):
|
||||
"""
|
||||
Install the MS SQL Server ODBC driver
|
||||
|
||||
https://docs.microsoft.com/en-us/sql/connect/odbc/linux-mac/installing-the-microsoft-odbc-driver-for-sql-server?view=sql-server-2017
|
||||
"""
|
||||
apt.install(c, 'apt-transport-https', 'curl')
|
||||
c.sudo("bash -c 'curl https://packages.microsoft.com/keys/microsoft.asc | apt-key add -'")
|
||||
c.sudo("bash -c 'curl https://packages.microsoft.com/config/debian/9/prod.list > /etc/apt/sources.list.d/mssql-release.list'")
|
||||
apt.update(c)
|
||||
cmd = 'apt-get --assume-yes install msodbcsql17'
|
||||
if accept_eula:
|
||||
cmd = 'ACCEPT_EULA=Y {}'.format(cmd)
|
||||
c.sudo(cmd)
|
|
@ -2,7 +2,7 @@
|
|||
################################################################################
|
||||
#
|
||||
# Rattail -- Retail Software Framework
|
||||
# Copyright © 2010-2020 Lance Edgar
|
||||
# Copyright © 2010-2023 Lance Edgar
|
||||
#
|
||||
# This file is part of Rattail.
|
||||
#
|
||||
|
@ -24,6 +24,8 @@
|
|||
Fabric Library for MySQL
|
||||
"""
|
||||
|
||||
import re
|
||||
|
||||
from rattail_fabric2 import apt, make_deploy, sed
|
||||
|
||||
|
||||
|
@ -38,6 +40,18 @@ def install(c):
|
|||
apt.install(c, 'default-mysql-server')
|
||||
|
||||
|
||||
def get_version_string(c):
|
||||
"""
|
||||
Fetch the version of MySQL running on the target system
|
||||
"""
|
||||
result = c.sudo('mysql --version', warn=True)
|
||||
if not result.failed:
|
||||
# match = re.match(r'^mysql .*?(\d+\.\d+\.\d+)-MariaDB', result.stdout)
|
||||
match = re.match(r'^mysql +Ver +(\d+\.\d+\.\d+)-.*', result.stdout)
|
||||
if match:
|
||||
return match.group(1)
|
||||
|
||||
|
||||
def set_bind_address(c, address):
|
||||
"""
|
||||
Configure the 'bind-address' setting with the given value.
|
||||
|
@ -70,8 +84,15 @@ def create_user(c, name, host='localhost', password=None, checkfirst=True):
|
|||
if not checkfirst or not user_exists(c, name, host):
|
||||
sql(c, "CREATE USER '{}'@'{}';".format(name, host))
|
||||
if password:
|
||||
sql(c, "SET PASSWORD FOR '{}'@'{}' = PASSWORD('{}');".format(
|
||||
name, host, password), hide=True, echo=False)
|
||||
# supposedly this is the new way to do it
|
||||
result = sql(c, "ALTER USER '{}'@'{}' IDENTIFIED BY '{}';".format(
|
||||
name, host, password),
|
||||
echo=False, hide=True, warn=True)
|
||||
if result.failed: # but this may fail for older systems
|
||||
# in which case we try it the old way
|
||||
sql(c, "SET PASSWORD FOR '{}'@'{}' = PASSWORD('{}');".format(
|
||||
name, host, password),
|
||||
echo=False, hide=True)
|
||||
|
||||
|
||||
def db_exists(c, name):
|
||||
|
@ -142,18 +163,31 @@ def script(c, path, database=''):
|
|||
c.sudo("bash -c 'mysql {} < {}'".format(database, path))
|
||||
|
||||
|
||||
def download_db(c, name, destination=None):
|
||||
def dump_db(c, name, skip_triggers=False):
|
||||
"""
|
||||
Dump a database to file, on the server represented by ``c`` param.
|
||||
|
||||
This function returns the name of the DB dump file. The name will
|
||||
not have a path component as it's assumed to be in the home folder
|
||||
of the connection user.
|
||||
"""
|
||||
skip_triggers = '--skip-triggers' if skip_triggers else ''
|
||||
# note, we force sudo "as root" to ensure -H flag is used
|
||||
# (which allows us to leverage /root/.my.cnf config file)
|
||||
c.sudo(f'mysqldump {skip_triggers} --result-file={name}.sql {name}',
|
||||
user='root')
|
||||
c.sudo(f'gzip --force {name}.sql')
|
||||
return f'{name}.sql.gz'
|
||||
|
||||
|
||||
def download_db(c, name, destination=None, **kwargs):
|
||||
"""
|
||||
Download a database from the "current" server.
|
||||
"""
|
||||
if destination is None:
|
||||
destination = './{}.sql.gz'.format(name)
|
||||
# note, we force sudo "as root" to ensure -H flag is used
|
||||
# (which allows us to leverage /root/.my.cnf config file)
|
||||
c.sudo('mysqldump --result-file={0}.sql {0}'.format(name), user='root')
|
||||
c.sudo('gzip --force {}.sql'.format(name))
|
||||
c.get('{}.sql.gz'.format(name), destination)
|
||||
c.sudo('rm {}.sql.gz'.format(name))
|
||||
filename = dump_db(c, name,
|
||||
skip_triggers=kwargs.get('skip_triggers', False))
|
||||
c.get(filename, destination or f'./{filename}')
|
||||
c.sudo(f'rm {filename}')
|
||||
|
||||
|
||||
def clone_db(c, name, download, user=None, force=False):
|
||||
|
@ -184,3 +218,20 @@ def clone_db(c, name, download, user=None, force=False):
|
|||
c.run('gunzip --force {}.sql.gz'.format(name))
|
||||
c.sudo("bash -c 'mysql {0} < {0}.sql'".format(name))
|
||||
c.run('rm {}.sql'.format(name))
|
||||
|
||||
|
||||
def restore_db(c, name, path):
|
||||
"""
|
||||
Restore a database from a dump file.
|
||||
|
||||
:param name: Name of the database to restore.
|
||||
|
||||
:param path: Path to the DB dump file, which should end in ``.sql.gz``
|
||||
"""
|
||||
if not path.endswith('.sql.gz'):
|
||||
raise ValueError("Path to dump file must end in `.sql.gz`")
|
||||
|
||||
c.sudo(f'gunzip --force {path}')
|
||||
sql_path = path[:-3]
|
||||
c.sudo(f"bash -c 'mysql {name} < {sql_path}'")
|
||||
c.sudo(f'rm {sql_path}')
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
################################################################################
|
||||
#
|
||||
# Rattail -- Retail Software Framework
|
||||
# Copyright © 2010-2019 Lance Edgar
|
||||
# Copyright © 2010-2023 Lance Edgar
|
||||
#
|
||||
# This file is part of Rattail.
|
||||
#
|
||||
|
@ -46,6 +46,9 @@ def install(c, version=None, user=None):
|
|||
|
||||
profile = os.path.join(home, '.profile')
|
||||
kwargs = {'use_sudo': bool(user)}
|
||||
if kwargs['use_sudo']:
|
||||
c.sudo(f'touch {profile}')
|
||||
c.sudo(f'chown {user}: {profile}')
|
||||
append(c, profile, 'export NVM_DIR="{}"'.format(nvm), **kwargs)
|
||||
append(c, profile, '[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"', **kwargs)
|
||||
append(c, profile, '[ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion"', **kwargs)
|
||||
|
|
|
@ -38,6 +38,6 @@ def install_pod(c, path='/srv/pod', download_url=None):
|
|||
if not exists(c, os.path.join(path, 'pod_pictures_gtin.zip')):
|
||||
if not download_url:
|
||||
download_url = 'http://www.product-open-data.com/docs/pod_pictures_gtin_2013.08.29_01.zip'
|
||||
c.sudo("bash -c 'cd {}; wget --output-document=pod_pictures_gtin.zip {}'".format(path, url))
|
||||
c.sudo("bash -c 'cd {}; wget --output-document=pod_pictures_gtin.zip {}'".format(path, download_url))
|
||||
if not exists(c, os.path.join(path, 'pictures/gtin')):
|
||||
c.sudo("bash -c 'cd {}; unzip pod_pictures_gtin.zip -d pictures'".format(path))
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
################################################################################
|
||||
#
|
||||
# Rattail -- Retail Software Framework
|
||||
# Copyright © 2010-2019 Lance Edgar
|
||||
# Copyright © 2010-2022 Lance Edgar
|
||||
#
|
||||
# This file is part of Rattail.
|
||||
#
|
||||
|
@ -25,6 +25,7 @@ Fabric library for Postfix
|
|||
"""
|
||||
|
||||
from rattail_fabric2 import apt
|
||||
from rattail.util import shlex_join
|
||||
|
||||
|
||||
def install(c):
|
||||
|
@ -42,10 +43,17 @@ def alias(c, name, alias_to, path='/etc/aliases'):
|
|||
# does alias entry already exist?
|
||||
if c.run("grep '^{}:' /etc/aliases".format(name), warn=True).failed:
|
||||
# append new entry
|
||||
c.sudo("""bash -c 'echo "{}: {}" >> /etc/aliases'""".format(name, alias_to))
|
||||
entry = '{}: {}'.format(name, alias_to)
|
||||
echo = shlex_join(['echo', entry])
|
||||
cmd = '{} >> /etc/aliases'.format(echo)
|
||||
cmd = shlex_join(['bash', '-c', cmd])
|
||||
c.sudo(cmd)
|
||||
else:
|
||||
# update existing entry
|
||||
c.sudo('sed -i.bak -e "s/^{}: .*/{}: {}/" /etc/aliases'.format(name, name, alias_to))
|
||||
alias_to = alias_to.replace('|', '\\|')
|
||||
sub = "s|^{}: .*|{}: {}|".format(name, name, alias_to)
|
||||
cmd = shlex_join(['sed', '-i.bak', '-E', sub, '/etc/aliases'])
|
||||
c.sudo(cmd)
|
||||
|
||||
c.sudo('newaliases')
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
################################################################################
|
||||
#
|
||||
# Rattail -- Retail Software Framework
|
||||
# Copyright © 2010-2019 Lance Edgar
|
||||
# Copyright © 2010-2023 Lance Edgar
|
||||
#
|
||||
# This file is part of Rattail.
|
||||
#
|
||||
|
@ -27,14 +27,14 @@ Fabric Library for PostgreSQL
|
|||
import os
|
||||
import re
|
||||
|
||||
from rattail_fabric2 import apt
|
||||
from rattail_fabric2 import apt, append, contains, sed, uncomment
|
||||
|
||||
|
||||
def install(c):
|
||||
"""
|
||||
Install the PostgreSQL database service
|
||||
"""
|
||||
apt.install(c, 'postgresql')
|
||||
apt.install(c, 'postgresql', 'libpq-dev')
|
||||
|
||||
|
||||
def get_version(c):
|
||||
|
@ -47,6 +47,48 @@ def get_version(c):
|
|||
if match:
|
||||
return float(match.group(1))
|
||||
|
||||
|
||||
def set_listen_addresses(c, *addresses):
|
||||
"""
|
||||
Overwrite the `listen_addresses` config setting in `postgresql.conf`.
|
||||
"""
|
||||
version = get_version(c)
|
||||
if version > 12:
|
||||
version = int(version)
|
||||
|
||||
addresses = ','.join(addresses)
|
||||
|
||||
if not contains(c, '/etc/postgresql/{}/main/postgresql.conf'.format(version),
|
||||
"listen_addresses = '{}'".format(addresses),
|
||||
exact=True):
|
||||
|
||||
uncomment(c, '/etc/postgresql/{}/main/postgresql.conf'.format(version),
|
||||
r'^# *listen_addresses\s*=.*',
|
||||
use_sudo=True)
|
||||
|
||||
sed(c, '/etc/postgresql/{}/main/postgresql.conf'.format(version),
|
||||
r'listen_addresses\s*=.*',
|
||||
"listen_addresses = '{}'".format(addresses),
|
||||
use_sudo=True)
|
||||
|
||||
restart(c)
|
||||
|
||||
|
||||
def add_hba_entry(c, entry):
|
||||
"""
|
||||
Add an entry to the `pg_hba.conf` file.
|
||||
"""
|
||||
version = get_version(c)
|
||||
if version > 12:
|
||||
version = int(version)
|
||||
|
||||
if not contains(c, '/etc/postgresql/{}/main/pg_hba.conf'.format(version),
|
||||
entry, use_sudo=True):
|
||||
append(c, '/etc/postgresql/{}/main/pg_hba.conf'.format(version),
|
||||
entry, use_sudo=True)
|
||||
reload_(c)
|
||||
|
||||
|
||||
def restart(c):
|
||||
"""
|
||||
Restart the PostgreSQL database service
|
||||
|
@ -54,12 +96,15 @@ def restart(c):
|
|||
c.sudo('systemctl restart postgresql.service')
|
||||
|
||||
|
||||
def reload(c):
|
||||
def reload_(c):
|
||||
"""
|
||||
Reload config for the PostgreSQL database service
|
||||
"""
|
||||
c.sudo('systemctl reload postgresql.service')
|
||||
|
||||
# TODO: deprecate / remove this
|
||||
reload = reload_
|
||||
|
||||
|
||||
def sql(c, sql, database='', port=None, **kwargs):
|
||||
"""
|
||||
|
@ -71,6 +116,23 @@ def sql(c, sql, database='', port=None, **kwargs):
|
|||
return c.sudo(cmd, user='postgres', **kwargs)
|
||||
|
||||
|
||||
def script(c, path, database='', port=None, user=None, password=None):
|
||||
"""
|
||||
Execute a SQL script. By default this will run as 'postgres' user, but can
|
||||
use PGPASSWORD authentication if necessary.
|
||||
"""
|
||||
port = '--port={}'.format(port) if port else ''
|
||||
if user and password:
|
||||
kw = dict(pw=password, user=user, port=port, path=path, db=database)
|
||||
return c.sudo(" PGPASSWORD='{pw}' psql --host=localhost {port} --username='{user}' --file='{path}' {db}".format(**kw),
|
||||
echo=False)
|
||||
|
||||
else: # run as postgres
|
||||
kw = dict(port=port, path=path, db=database)
|
||||
return c.sudo("psql {port} --file='{path}' {db}".format(**kw),
|
||||
user='postgres')
|
||||
|
||||
|
||||
def user_exists(c, name, port=None):
|
||||
"""
|
||||
Determine if a given PostgreSQL user exists.
|
||||
|
@ -137,22 +199,52 @@ def drop_db(c, name, checkfirst=True):
|
|||
c.sudo('dropdb {}'.format(name), user='postgres')
|
||||
|
||||
|
||||
def download_db(c, name, destination=None, port=None, exclude_tables=None):
|
||||
def dump_db(c, name, port=None, exclude_tables=None,
|
||||
skip_raw_file=False):
|
||||
"""
|
||||
Dump a database to file, on the server represented by ``c`` param.
|
||||
|
||||
This function returns the name of the DB dump file. The name will not have
|
||||
a path component as it's assumed to be in the home folder of the connection
|
||||
user.
|
||||
"""
|
||||
c.run('touch {}.sql'.format(name))
|
||||
c.run('chmod 0666 {}.sql'.format(name))
|
||||
|
||||
sql_name = f'{name}.sql'
|
||||
gz_name = f'{sql_name}.gz'
|
||||
filename = gz_name if skip_raw_file else sql_name
|
||||
|
||||
port = f'--port={port}' if port else ''
|
||||
exclude_tables = f'--exclude-table-data={exclude_tables}' if exclude_tables else ''
|
||||
filename = '' if skip_raw_file else f'--file={filename}'
|
||||
cmd = f'pg_dump {port} {exclude_tables} {filename} {name}'
|
||||
|
||||
if skip_raw_file:
|
||||
tmp_name = f'/tmp/{gz_name}'
|
||||
cmd = f'{cmd} | gzip -c > {tmp_name}'
|
||||
c.sudo(cmd, user='postgres')
|
||||
# TODO: should remove this file
|
||||
c.run(f"cp {tmp_name} {gz_name}")
|
||||
|
||||
else:
|
||||
c.sudo(cmd, user='postgres')
|
||||
c.run(f'gzip --force {sql_name}')
|
||||
|
||||
return gz_name
|
||||
|
||||
|
||||
def download_db(c, name, destination=None, port=None, exclude_tables=None,
|
||||
skip_raw_file=False):
|
||||
"""
|
||||
Download a database from the server represented by ``c`` param.
|
||||
"""
|
||||
if destination is None:
|
||||
destination = './{}.sql.gz'.format(name)
|
||||
c.run('touch {}.sql'.format(name))
|
||||
c.run('chmod 0666 {}.sql'.format(name))
|
||||
cmd = 'pg_dump {port} {exclude_tables} --file={name}.sql {name}'.format(
|
||||
name=name,
|
||||
port='--port={}'.format(port) if port else '',
|
||||
exclude_tables='--exclude-table-data={}'.format(exclude_tables) if exclude_tables else '')
|
||||
c.sudo(cmd, user='postgres')
|
||||
c.run('gzip --force {}.sql'.format(name))
|
||||
c.get('{}.sql.gz'.format(name), destination)
|
||||
c.run('rm {}.sql.gz'.format(name))
|
||||
dumpfile = dump_db(c, name, port=port, exclude_tables=exclude_tables,
|
||||
skip_raw_file=skip_raw_file)
|
||||
c.get(dumpfile, destination)
|
||||
c.run('rm {}'.format(dumpfile))
|
||||
|
||||
|
||||
def clone_db(c, name, owner, download, user='rattail', force=False, workdir=None):
|
||||
|
@ -185,6 +277,27 @@ def clone_db(c, name, owner, download, user='rattail', force=False, workdir=None
|
|||
os.chdir(curdir)
|
||||
|
||||
# restore database on target server
|
||||
c.run('gunzip --force {}.sql.gz'.format(name))
|
||||
c.sudo('psql --echo-errors --file={0}.sql {0}'.format(name), user='postgres')
|
||||
c.run('rm {}.sql'.format(name))
|
||||
# TODO: first tried c.sudo('mv ...') but that did not work for the "typical"
|
||||
# scenario of connecting as rattail@server to obtain db dump, since the dump
|
||||
# cmd is normally carved out via sudoers config, but 'sudo mv ..' is not
|
||||
filename = f'{name}.sql.gz'
|
||||
c.run(f'mv {filename} /tmp/')
|
||||
restore_db(c, name, f'/tmp/{filename}')
|
||||
|
||||
|
||||
def restore_db(c, name, path):
|
||||
"""
|
||||
Restore a database from a dump file.
|
||||
|
||||
:param name: Name of the database to restore.
|
||||
|
||||
:param path: Path to the DB dump file, which should end in ``.sql.gz``
|
||||
"""
|
||||
if not path.endswith('.sql.gz'):
|
||||
raise ValueError("Path to dump file must end in `.sql.gz`")
|
||||
|
||||
c.run('gunzip --force {}'.format(path))
|
||||
sql_path = path[:-3]
|
||||
c.sudo('psql --echo-errors --file={} {}'.format(sql_path, name),
|
||||
user='postgres')
|
||||
c.run('rm {}'.format(sql_path))
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
################################################################################
|
||||
#
|
||||
# Rattail -- Retail Software Framework
|
||||
# Copyright © 2010-2020 Lance Edgar
|
||||
# Copyright © 2010-2023 Lance Edgar
|
||||
#
|
||||
# This file is part of Rattail.
|
||||
#
|
||||
|
@ -26,31 +26,44 @@ Fabric Library for Python
|
|||
|
||||
from contextlib import contextmanager
|
||||
|
||||
from rattail_fabric2 import apt, exists, make_deploy, mkdir
|
||||
from rattail_fabric2 import apt, append, exists, make_deploy, mkdir
|
||||
|
||||
|
||||
deploy = make_deploy(__file__)
|
||||
deploy_common = make_deploy(__file__)
|
||||
|
||||
|
||||
def bootstrap_python(c, pip_from_apt=True, pip_eager=True,
|
||||
def bootstrap_python(c, deploy=None,
|
||||
pip_from_apt=True,
|
||||
pip_eager=True,
|
||||
pip_method=None,
|
||||
pip_auto=False,
|
||||
pip_package_name=None,
|
||||
virtualenvwrapper_from_apt=False,
|
||||
upgrade_virtualenvwrapper=True,
|
||||
workon_home='/srv/envs', user='rattail'):
|
||||
workon_home='/srv/envs',
|
||||
user='rattail',
|
||||
python3=False):
|
||||
"""
|
||||
Bootstrap a "complete" Python install.
|
||||
"""
|
||||
# build dependencies
|
||||
apt.install(
|
||||
c,
|
||||
'python-dev',
|
||||
'python3-dev',
|
||||
'python3-venv',
|
||||
'libffi-dev',
|
||||
'libjpeg-dev',
|
||||
'libssl-dev',
|
||||
)
|
||||
if not python3:
|
||||
apt.install(c, 'python-dev')
|
||||
|
||||
# pip
|
||||
install_pip(c, use_apt=pip_from_apt, eager=pip_eager)
|
||||
install_pip(c, method=pip_method, python3=python3,
|
||||
auto=pip_auto,
|
||||
use_apt=pip_from_apt,
|
||||
apt_package_name=pip_package_name,
|
||||
eager=pip_eager)
|
||||
|
||||
# virtualenvwrapper
|
||||
workon_home = workon_home.rstrip('/')
|
||||
|
@ -58,7 +71,8 @@ def bootstrap_python(c, pip_from_apt=True, pip_eager=True,
|
|||
use_apt=virtualenvwrapper_from_apt,
|
||||
upgrade=upgrade_virtualenvwrapper,
|
||||
configure_me=False)
|
||||
deploy(c, 'python/premkvirtualenv', '{}/premkvirtualenv'.format(workon_home), owner=user, use_sudo=True)
|
||||
# TODO: let custom deploy override this
|
||||
deploy_common(c, 'python/premkvirtualenv', '{}/premkvirtualenv'.format(workon_home), owner=user, use_sudo=True)
|
||||
|
||||
|
||||
def install_pythonz(c):
|
||||
|
@ -72,6 +86,7 @@ def install_pythonz(c):
|
|||
'curl',
|
||||
# these are needed when building python:
|
||||
'libsqlite3-dev',
|
||||
'libssl-dev',
|
||||
'zlib1g-dev',
|
||||
)
|
||||
if not exists(c, '/usr/local/pythonz'):
|
||||
|
@ -83,7 +98,23 @@ def install_pythonz(c):
|
|||
c.sudo('/usr/local/src/pythonz/pythonz-install')
|
||||
|
||||
|
||||
def install_python(c, version, globally=False, verbose=False):
|
||||
def configure_pythonz(c, user):
|
||||
"""
|
||||
Configure the given user's ``.bashrc`` to allow use of pythonz.
|
||||
"""
|
||||
# TODO: should normalize this get_home_dir() logic?
|
||||
home = c.run('getent passwd {} | cut -d: -f6'.format(user)).stdout.strip()
|
||||
home = home.rstrip('/')
|
||||
|
||||
script = '{}/.bashrc'.format(home)
|
||||
append(c, script, 'export PYTHONZ_ROOT=/usr/local/pythonz',
|
||||
use_sudo=True)
|
||||
append(c, script, '[[ -s /usr/local/pythonz/etc/bashrc ]] && source /usr/local/pythonz/etc/bashrc',
|
||||
use_sudo=True)
|
||||
|
||||
|
||||
def install_python(c, version, globally=False, verbose=False,
|
||||
use_clang=False):
|
||||
"""
|
||||
Install a specific version of python, via pythonz.
|
||||
|
||||
|
@ -92,23 +123,67 @@ def install_python(c, version, globally=False, verbose=False):
|
|||
symlink, if installed, will use the "short" version, e.g. if the
|
||||
``version`` specified is ``'3.5.3'`` then the symlink will be named
|
||||
``'python3.5'``.
|
||||
|
||||
:param use_clang: Use `clang` instead of default compiler. May be
|
||||
needed in rare cases for older python versions. See also
|
||||
https://stackoverflow.com/a/73267352
|
||||
"""
|
||||
if use_clang:
|
||||
apt.install(c, 'clang')
|
||||
if not exists(c, '/usr/local/pythonz/pythons/CPython-{}'.format(version)):
|
||||
clang = 'CC=clang' if use_clang else ''
|
||||
verbose = '--verbose' if verbose else ''
|
||||
c.sudo("bash -lc 'pythonz install {} {}'".format(verbose, version))
|
||||
c.sudo(f"bash -lc '{clang} pythonz install {verbose} {version}'")
|
||||
if globally:
|
||||
short_version = '.'.join(version.split('.')[:2])
|
||||
c.sudo('ln -sf /usr/local/pythonz/pythons/CPython-{0}/bin/python{1} /usr/local/bin/python{1}'.format(
|
||||
version, short_version))
|
||||
|
||||
|
||||
def install_pip(c, use_apt=False, eager=True):
|
||||
def install_pip(c, method=None,
|
||||
auto=False, python3=False,
|
||||
use_apt=False, apt_package_name=None,
|
||||
eager=True):
|
||||
"""
|
||||
Install/upgrade the Pip installer for Python.
|
||||
"""
|
||||
if use_apt:
|
||||
apt.install(c, 'python-pip')
|
||||
else:
|
||||
# first check for existing pip; we do nothing if already present
|
||||
pip_ = 'pip3' if python3 else 'pip2'
|
||||
if not c.sudo('which {}'.format(pip_), warn=True).failed:
|
||||
return
|
||||
|
||||
if method == 'apt':
|
||||
package = apt_package_name
|
||||
if not package:
|
||||
package = 'python3-pip' if python3 else 'python-pip'
|
||||
apt.install(c, package, warn=True)
|
||||
|
||||
elif method == 'get-pip':
|
||||
url = 'https://bootstrap.pypa.io/get-pip.py'
|
||||
if not python3:
|
||||
url = 'https://bootstrap.pypa.io/pip/2.7/get-pip.py'
|
||||
c.sudo('wget -O get-pip.py {}'.format(url))
|
||||
python = 'python3' if python3 else 'python2'
|
||||
c.sudo('{} get-pip.py'.format(python))
|
||||
c.sudo('rm get-pip.py')
|
||||
|
||||
elif auto: # try apt first, then fall back to get-pip.py
|
||||
package = apt_package_name
|
||||
if not package:
|
||||
package = 'python3-pip' if python3 else 'python-pip'
|
||||
result = apt.install(c, package, warn=True)
|
||||
if result.failed:
|
||||
c.sudo('wget -O get-pip.py https://bootstrap.pypa.io/get-pip.py')
|
||||
python = 'python3' if python3 else 'python2'
|
||||
c.sudo('{} get-pip.py'.format(python))
|
||||
c.sudo('rm get-pip.py')
|
||||
|
||||
elif use_apt: # use apt only, w/ no fallback
|
||||
if not apt_package_name:
|
||||
apt_package_name = 'python3-pip' if python3 else 'python-pip'
|
||||
apt.install(c, apt_package_name)
|
||||
|
||||
else: # *deprecated* method using easy_install
|
||||
apt.install(c, 'build-essential', 'python-dev', 'libssl-dev', 'libffi-dev')
|
||||
if c.run('which pip', warn=True).failed:
|
||||
apt.install(c, 'python-pkg-resources', 'python-setuptools')
|
||||
|
@ -157,9 +232,12 @@ def install_virtualenvwrapper(c, workon_home='/srv/envs', user='root',
|
|||
mkdir(c, workon_home, owner=user, use_sudo=True)
|
||||
if use_apt:
|
||||
apt.install(c, 'virtualenvwrapper')
|
||||
configure_virtualenvwrapper(c, user, workon_home,
|
||||
wrapper='/usr/share/virtualenvwrapper/virtualenvwrapper.sh')
|
||||
else:
|
||||
pip(c, 'virtualenvwrapper', upgrade=upgrade)
|
||||
configure_virtualenvwrapper(c, user, workon_home)
|
||||
configure_virtualenvwrapper(c, user, workon_home,
|
||||
wrapper='/usr/local/bin/virtualenvwrapper.sh')
|
||||
if configure_me:
|
||||
# TODO
|
||||
# configure_virtualenvwrapper(c, env.user, workon_home)
|
||||
|
@ -167,6 +245,7 @@ def install_virtualenvwrapper(c, workon_home='/srv/envs', user='root',
|
|||
|
||||
|
||||
def configure_virtualenvwrapper(c, user, workon_home='/srv/envs',
|
||||
# TODO: this default should change, per apt
|
||||
wrapper='/usr/local/bin/virtualenvwrapper.sh'):
|
||||
"""
|
||||
Configure virtualenvwrapper for the given user account.
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
################################################################################
|
||||
#
|
||||
# Rattail -- Retail Software Framework
|
||||
# Copyright © 2010-2018 Lance Edgar
|
||||
# Copyright © 2010-2021 Lance Edgar
|
||||
#
|
||||
# This file is part of Rattail.
|
||||
#
|
||||
|
@ -28,49 +28,94 @@ from __future__ import unicode_literals, absolute_import
|
|||
|
||||
import os
|
||||
|
||||
from rattail_fabric2 import postgresql, make_deploy, make_system_user, mkdir
|
||||
from rattail_fabric2 import apache, apt, postfix, postgresql, python, make_deploy, make_system_user, mkdir
|
||||
|
||||
|
||||
deploy = make_deploy(__file__)
|
||||
deploy_common = make_deploy(__file__)
|
||||
|
||||
|
||||
def bootstrap_rattail(c, home='/var/lib/rattail', uid=None, shell='/bin/bash'):
|
||||
def bootstrap_rattail_base(c, deploy=None, timezone='America/Chicago',
|
||||
**context):
|
||||
"""
|
||||
Bootstrap the base system requirements, common to all machines
|
||||
running Rattail apps. Note that this includes basic installation
|
||||
of Python, PostgreSQL and Aapche.
|
||||
"""
|
||||
env = context.get('env')
|
||||
context['timezone'] = timezone
|
||||
|
||||
# make system user, install init scripts etc.
|
||||
bootstrap_rattail(c, alias='root')
|
||||
|
||||
# machine-wide config
|
||||
if deploy and deploy.local_exists('rattail/rattail.conf.mako'):
|
||||
deploy(c, 'rattail/rattail.conf.mako', '/etc/rattail/rattail.conf',
|
||||
use_sudo=True, context=context)
|
||||
else:
|
||||
deploy_machine_conf(c, timezone=timezone, context=context)
|
||||
|
||||
# python
|
||||
python.bootstrap_python(c, deploy,
|
||||
python3=True,
|
||||
virtualenvwrapper_from_apt=True)
|
||||
|
||||
# postgres
|
||||
postgresql.install(c)
|
||||
if env and hasattr(env, 'password_postgresql_rattail'):
|
||||
postgresql.create_user(c, 'rattail', password=env.password_postgresql_rattail)
|
||||
|
||||
# apache
|
||||
apache.install(c)
|
||||
apache.enable_mod(c, 'proxy_http')
|
||||
|
||||
# supervisor
|
||||
apt.install(c, 'supervisor')
|
||||
|
||||
|
||||
def bootstrap_rattail(c, home='/var/lib/rattail', uid=None, shell='/bin/bash',
|
||||
alias=None):
|
||||
"""
|
||||
Bootstrap a basic Rattail software environment.
|
||||
"""
|
||||
make_system_user(c, 'rattail', home=home, uid=uid, shell=shell)
|
||||
mkdir(c, os.path.join(home, '.ssh'), owner='rattail:', mode='0700', use_sudo=True)
|
||||
if alias:
|
||||
postfix.alias(c, 'rattail', alias)
|
||||
|
||||
mkdir(c, '/etc/rattail', use_sudo=True)
|
||||
mkdir(c, '/srv/rattail', use_sudo=True)
|
||||
mkdir(c, '/var/log/rattail', owner='rattail:rattail', mode='0775', use_sudo=True)
|
||||
|
||||
mkdir(c, '/srv/rattail/init', use_sudo=True)
|
||||
deploy(c, 'daemon', '/srv/rattail/init/daemon', use_sudo=True)
|
||||
deploy(c, 'check-rattail-daemon', '/usr/local/bin/check-rattail-daemon', use_sudo=True)
|
||||
deploy(c, 'check-supervisor-process', '/usr/local/bin/check-supervisor-process', use_sudo=True)
|
||||
deploy(c, 'check-systemd-service', '/usr/local/bin/check-systemd-service', use_sudo=True)
|
||||
deploy(c, 'luigid', '/srv/rattail/init/luigid', use_sudo=True)
|
||||
deploy(c, 'soffice', '/srv/rattail/init/soffice', use_sudo=True)
|
||||
# TODO: deprecate / remove these
|
||||
deploy(c, 'bouncer', '/srv/rattail/init/bouncer', use_sudo=True)
|
||||
deploy(c, 'datasync', '/srv/rattail/init/datasync', use_sudo=True)
|
||||
deploy(c, 'filemon', '/srv/rattail/init/filemon', use_sudo=True)
|
||||
deploy_common(c, 'check-rattail-daemon', '/usr/local/bin/check-rattail-daemon', use_sudo=True)
|
||||
deploy_common(c, 'check-supervisor-process', '/usr/local/bin/check-supervisor-process', use_sudo=True)
|
||||
deploy_common(c, 'check-systemd-service', '/usr/local/bin/check-systemd-service', use_sudo=True)
|
||||
deploy_common(c, 'soffice', '/srv/rattail/init/soffice', use_sudo=True)
|
||||
|
||||
|
||||
def deploy_machine_conf(c, timezone='America/Chicago'):
|
||||
def deploy_machine_conf(c, env=None, timezone=None, context={}):
|
||||
"""
|
||||
Deploy the standard machine-wide ``rattail.conf`` file.
|
||||
"""
|
||||
mkdir(c, '/etc/rattail', use_sudo=True)
|
||||
deploy(c, 'rattail/rattail.conf.mako', '/etc/rattail/rattail.conf', use_sudo=True,
|
||||
context={'timezone': timezone})
|
||||
context['env'] = env
|
||||
context['timezone'] = timezone or getattr(env, 'timezone', 'America/Chicago')
|
||||
deploy_common(c, 'rattail/rattail.conf.mako', '/etc/rattail/rattail.conf', use_sudo=True,
|
||||
context=context)
|
||||
|
||||
|
||||
def delete_email_recipients(c, dbname):
|
||||
"""
|
||||
Purge all email recipient settings for the given database.
|
||||
"""
|
||||
# purge new-style for wuttjamaican
|
||||
postgresql.sql(c, "delete from setting where name like 'rattail.email.%.sender';", database=dbname)
|
||||
postgresql.sql(c, "delete from setting where name like 'rattail.email.%.to';", database=dbname)
|
||||
postgresql.sql(c, "delete from setting where name like 'rattail.email.%.cc';", database=dbname)
|
||||
postgresql.sql(c, "delete from setting where name like 'rattail.email.%.bcc';", database=dbname)
|
||||
|
||||
# purge old-style for rattail
|
||||
postgresql.sql(c, "delete from setting where name like 'rattail.mail.%.from';", database=dbname)
|
||||
postgresql.sql(c, "delete from setting where name like 'rattail.mail.%.to';", database=dbname)
|
||||
postgresql.sql(c, "delete from setting where name like 'rattail.mail.%.cc';", database=dbname)
|
||||
postgresql.sql(c, "delete from setting where name like 'rattail.mail.%.bcc';", database=dbname)
|
||||
|
@ -81,3 +126,17 @@ def disable_emails(c, dbname):
|
|||
Disable all emails for the given database.
|
||||
"""
|
||||
postgresql.sql(c, "update setting set value = 'false' where name like 'rattail.mail.%.enabled';", database=dbname)
|
||||
|
||||
|
||||
def deploy_datasync_checks(c, envroot, **kwargs):
|
||||
"""
|
||||
Deploy the standard datasync (queue, watcher) check scripts.
|
||||
"""
|
||||
envroot = envroot.rstrip('/')
|
||||
context = kwargs.pop('context', {})
|
||||
context['envroot'] = envroot
|
||||
context.setdefault('config', kwargs.pop('config', 'quiet'))
|
||||
deploy_common(c, 'rattail/check-datasync-queue.mako', '{}/app/check-datasync-queue'.format(envroot),
|
||||
context=context, **kwargs)
|
||||
deploy_common(c, 'rattail/check-datasync-watchers.mako', '{}/app/check-datasync-watchers'.format(envroot),
|
||||
context=context, **kwargs)
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
################################################################################
|
||||
#
|
||||
# Rattail -- Retail Software Framework
|
||||
# Copyright © 2010-2019 Lance Edgar
|
||||
# Copyright © 2010-2020 Lance Edgar
|
||||
#
|
||||
# This file is part of Rattail.
|
||||
#
|
||||
|
@ -25,13 +25,40 @@ Fabric Library for SSH
|
|||
"""
|
||||
|
||||
|
||||
def cache_host_key(c, host, for_user='root'):
|
||||
def cache_host_key(c, host, port=None, user=None, **kwargs):
|
||||
"""
|
||||
Cache the SSH host key for the given host, for the given user.
|
||||
"""
|
||||
cmd = 'ssh -o StrictHostKeyChecking=no {} echo'.format(host)
|
||||
user = None if for_user == 'root' else for_user
|
||||
c.sudo(cmd, user=user, warn=True)
|
||||
if 'for_user' in kwargs:
|
||||
pass # TODO: deprecation warning
|
||||
|
||||
if not user and kwargs.get('for_user'):
|
||||
user = kwargs['for_user']
|
||||
|
||||
port = '-p {}'.format(port) if port else ''
|
||||
|
||||
# first try to run basic command over ssh; if it works then we don't
|
||||
# actually need to update/cache the host key
|
||||
cmd = 'ssh {} {} whoami'.format(port, host)
|
||||
if user:
|
||||
result = c.sudo(cmd, warn=True, user=None if user == 'root' else user)
|
||||
else:
|
||||
result = c.run(cmd, warn=True)
|
||||
if result.failed:
|
||||
|
||||
# basic command failed, but in some cases that is simply b/c normal
|
||||
# commands are not allowed, although the ssh connection itself was
|
||||
# established okay. here we check for that situation.
|
||||
if "Disallowed command" not in result.stderr:
|
||||
|
||||
# okay then we now think that the ssh connection itself was not
|
||||
# made, which presumably means we *do* need to cache the host key,
|
||||
# so try that now
|
||||
cmd = 'ssh -o StrictHostKeyChecking=no {} {} whoami'.format(port, host)
|
||||
if user:
|
||||
c.sudo(cmd, user=None if user == 'root' else user, warn=True)
|
||||
else:
|
||||
c.run(cmd, warn=True)
|
||||
|
||||
|
||||
def restart(c):
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
################################################################################
|
||||
#
|
||||
# Rattail -- Retail Software Framework
|
||||
# Copyright © 2010-2019 Lance Edgar
|
||||
# Copyright © 2010-2023 Lance Edgar
|
||||
#
|
||||
# This file is part of Rattail.
|
||||
#
|
||||
|
@ -111,8 +111,6 @@ def append(c, filename, text, use_sudo=False, partial=False, escape=True,
|
|||
"""
|
||||
func = use_sudo and c.sudo or c.run
|
||||
# Normalize non-list input to be a list
|
||||
# TODO: do we need to check for six.something here?
|
||||
# if isinstance(text, basestring):
|
||||
if isinstance(text, str):
|
||||
text = [text]
|
||||
for line in text:
|
||||
|
@ -240,6 +238,10 @@ def sed(c, filename, before, after, limit='', use_sudo=False, backup='.bak',
|
|||
after = after.replace(char, r'\%s' % char)
|
||||
if limit:
|
||||
limit = r'/%s/ ' % limit
|
||||
|
||||
# if replacement text contains single quote chars, must escape them
|
||||
after = after.replace("'", "'\"'\"'")
|
||||
|
||||
context = {
|
||||
'script': r"'%ss/%s/%s/%sg'" % (limit, before, after, flags),
|
||||
'filename': _expand_path(c, filename),
|
||||
|
|
97
setup.py
97
setup.py
|
@ -1,97 +0,0 @@
|
|||
# -*- coding: utf-8; -*-
|
||||
################################################################################
|
||||
#
|
||||
# Rattail -- Retail Software Framework
|
||||
# Copyright © 2010-2018 Lance Edgar
|
||||
#
|
||||
# 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/>.
|
||||
#
|
||||
################################################################################
|
||||
|
||||
from __future__ import unicode_literals, absolute_import
|
||||
|
||||
import os
|
||||
from setuptools import setup, find_packages
|
||||
|
||||
|
||||
here = os.path.abspath(os.path.dirname(__file__))
|
||||
exec(open(os.path.join(here, 'rattail_fabric2', '_version.py')).read())
|
||||
README = open(os.path.join(here, 'README.rst')).read()
|
||||
|
||||
|
||||
requires = [
|
||||
#
|
||||
# Version numbers within comments below have specific meanings.
|
||||
# Basically the 'low' value is a "soft low," and 'high' a "soft high."
|
||||
# In other words:
|
||||
#
|
||||
# If either a 'low' or 'high' value exists, the primary point to be
|
||||
# made about the value is that it represents the most current (stable)
|
||||
# version available for the package (assuming typical public access
|
||||
# methods) whenever this project was started and/or documented.
|
||||
# Therefore:
|
||||
#
|
||||
# If a 'low' version is present, you should know that attempts to use
|
||||
# versions of the package significantly older than the 'low' version
|
||||
# may not yield happy results. (A "hard" high limit may or may not be
|
||||
# indicated by a true version requirement.)
|
||||
#
|
||||
# Similarly, if a 'high' version is present, and especially if this
|
||||
# project has laid dormant for a while, you may need to refactor a bit
|
||||
# when attempting to support a more recent version of the package. (A
|
||||
# "hard" low limit should be indicated by a true version requirement
|
||||
# when a 'high' version is present.)
|
||||
#
|
||||
# In any case, developers and other users are encouraged to play
|
||||
# outside the lines with regard to these soft limits. If bugs are
|
||||
# encountered then they should be filed as such.
|
||||
#
|
||||
# package # low high
|
||||
|
||||
'fabric2', # 2.4.0
|
||||
'invoke', # 1.2.0
|
||||
'six', # 1.11.0
|
||||
]
|
||||
|
||||
|
||||
setup(
|
||||
name = "rattail-fabric2",
|
||||
version = __version__,
|
||||
author = "Lance Edgar",
|
||||
author_email = "lance@edbob.org",
|
||||
url = "https://rattailproject.org/",
|
||||
license = "GNU GPL v3",
|
||||
description = "Fabric (v2) Utilities for Rattail",
|
||||
long_description = README,
|
||||
|
||||
classifiers = [
|
||||
'Development Status :: 3 - Alpha',
|
||||
'Environment :: Console',
|
||||
'Intended Audience :: Developers',
|
||||
'License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)',
|
||||
'Natural Language :: English',
|
||||
'Operating System :: OS Independent',
|
||||
'Programming Language :: Python :: 3',
|
||||
'Programming Language :: Python :: 3.5',
|
||||
'Topic :: System :: Systems Administration',
|
||||
'Topic :: Software Development :: Libraries :: Python Modules',
|
||||
],
|
||||
|
||||
install_requires = requires,
|
||||
packages = find_packages(),
|
||||
include_package_data = True,
|
||||
zip_safe = False,
|
||||
)
|
24
tasks.py
24
tasks.py
|
@ -2,7 +2,7 @@
|
|||
################################################################################
|
||||
#
|
||||
# Rattail -- Retail Software Framework
|
||||
# Copyright © 2010-2020 Lance Edgar
|
||||
# Copyright © 2010-2024 Lance Edgar
|
||||
#
|
||||
# This file is part of Rattail.
|
||||
#
|
||||
|
@ -24,16 +24,25 @@
|
|||
Tasks for rattail-fabric2
|
||||
"""
|
||||
|
||||
from __future__ import unicode_literals, absolute_import
|
||||
|
||||
import os
|
||||
import re
|
||||
import shutil
|
||||
|
||||
from invoke import task
|
||||
|
||||
|
||||
here = os.path.abspath(os.path.dirname(__file__))
|
||||
exec(open(os.path.join(here, 'rattail_fabric2', '_version.py')).read())
|
||||
__version__ = None
|
||||
pattern = re.compile(r'^version = "(\d+\.\d+\.\d+)"$')
|
||||
with open(os.path.join(here, 'pyproject.toml'), 'rt') as f:
|
||||
for line in f:
|
||||
line = line.rstrip('\n')
|
||||
match = pattern.match(line)
|
||||
if match:
|
||||
__version__ = match.group(1)
|
||||
break
|
||||
if not __version__:
|
||||
raise RuntimeError("could not parse version!")
|
||||
|
||||
|
||||
@task
|
||||
|
@ -41,9 +50,10 @@ def release(c):
|
|||
"""
|
||||
Release a new version of 'rattail-fabric2'.
|
||||
"""
|
||||
shutil.rmtree('rattail_fabric2.egg-info')
|
||||
if os.path.exists('rattail_fabric2.egg-info'):
|
||||
shutil.rmtree('rattail_fabric2.egg-info')
|
||||
# TODO: this seems heavy-handed? for sake of recursive-include in MANIFEST
|
||||
# TODO: what i esp. don't like is, this doesn't consider .gitignore
|
||||
c.run("find . -name '*~' -delete")
|
||||
c.run('python setup.py sdist --formats=gztar')
|
||||
c.run('twine upload dist/rattail-fabric2-{}.tar.gz'.format(__version__))
|
||||
c.run('python -m build --sdist')
|
||||
c.run(f'twine upload dist/rattail_fabric2-{__version__}.tar.gz')
|
||||
|
|
Loading…
Reference in a new issue