mirror of
https://github.com/YunoHost-Apps/pyinventory_ynh.git
synced 2024-09-03 20:16:09 +02:00
Refactor to use django_ynh
This commit is contained in:
parent
cca6eef925
commit
add1026c11
19 changed files with 173 additions and 440 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -6,3 +6,5 @@ __pycache__
|
|||
secret.txt
|
||||
/local_test/
|
||||
/poetry.lock
|
||||
/coverage.xml
|
||||
/htmlcov/
|
||||
|
|
15
Makefile
15
Makefile
|
@ -1,4 +1,5 @@
|
|||
SHELL := /bin/bash
|
||||
MAX_LINE_LENGTH := 119
|
||||
|
||||
all: help
|
||||
|
||||
|
@ -27,8 +28,18 @@ update: install-poetry ## update the sources and installation and generate "con
|
|||
poetry update
|
||||
poetry export -f requirements.txt --output conf/requirements.txt
|
||||
|
||||
local-test: check-poetry ## Run local_test.py to run the project locally
|
||||
poetry run ./local_test.py
|
||||
lint: ## Run code formatters and linter
|
||||
poetry run flynt --fail-on-change --line_length=${MAX_LINE_LENGTH} .
|
||||
poetry run isort --check-only .
|
||||
poetry run flake8 .
|
||||
|
||||
fix-code-style: ## Fix code formatting
|
||||
poetry run flynt --line_length=${MAX_LINE_LENGTH} .
|
||||
poetry run black --verbose --safe --line-length=${MAX_LINE_LENGTH} --skip-string-normalization .
|
||||
poetry run isort .
|
||||
|
||||
local-test: install ## Run local_test.py to run the project locally
|
||||
poetry run python3 ./local_test.py
|
||||
|
||||
local-diff-settings: ## Run "manage.py diffsettings" with local test
|
||||
poetry run python3 local_test/opt_yunohost/manage.py diffsettings
|
||||
|
|
|
@ -21,7 +21,7 @@ Pull requests welcome ;)
|
|||
|
||||
## Settings and upgrades
|
||||
|
||||
Almost everything related to PyInventory's configuration is handled in a `"../conf/ynh_pyinventory_settings.py"` file.
|
||||
Almost everything related to PyInventory's configuration is handled in a `"../conf/settings.py"` file.
|
||||
You can edit the file `$final_path/local_settings.py` to enable or disable features.
|
||||
|
||||
# Miscellaneous
|
||||
|
@ -100,13 +100,13 @@ drwxr-xr-x 3 root root 3 Dec 8 08:36 ..
|
|||
-rw-r--r-- 1 pyinventory pyinventory 171 Dec 8 08:39 secret.txt
|
||||
drwxr-xr-x 6 pyinventory pyinventory 6 Dec 8 08:37 venv
|
||||
-rw-r--r-- 1 pyinventory pyinventory 115 Dec 8 08:39 wsgi.py
|
||||
-rw-r--r-- 1 pyinventory pyinventory 4737 Dec 8 08:39 ynh_pyinventory_settings.py
|
||||
-rw-r--r-- 1 pyinventory pyinventory 4737 Dec 8 08:39 settings.py
|
||||
|
||||
root@yunohost:~# cd /opt/yunohost/pyinventory/
|
||||
root@yunohost:/opt/yunohost/pyinventory# source venv/bin/activate
|
||||
(venv) root@yunohost:/opt/yunohost/pyinventory# ./manage.py check
|
||||
PyInventory v0.8.2 (Django v2.2.17)
|
||||
DJANGO_SETTINGS_MODULE='ynh_pyinventory_settings'
|
||||
DJANGO_SETTINGS_MODULE='settings'
|
||||
PROJECT_PATH:/opt/yunohost/pyinventory/venv/lib/python3.7/site-packages
|
||||
BASE_PATH:/opt/yunohost/pyinventory
|
||||
System check identified no issues (0 silenced).
|
||||
|
|
|
@ -1,46 +0,0 @@
|
|||
#!/usr/bin/env python3
|
||||
import argparse
|
||||
import os
|
||||
import sys
|
||||
|
||||
|
||||
def main():
|
||||
os.environ['DJANGO_SETTINGS_MODULE'] = 'ynh_pyinventory_settings'
|
||||
|
||||
parser = argparse.ArgumentParser(
|
||||
description='Create or update Django super user.'
|
||||
)
|
||||
parser.add_argument('--username')
|
||||
parser.add_argument('--email')
|
||||
parser.add_argument('--password')
|
||||
|
||||
args = parser.parse_args()
|
||||
username = args.username
|
||||
email = args.email or ''
|
||||
password = args.password
|
||||
|
||||
import django
|
||||
django.setup()
|
||||
|
||||
from django.contrib.auth import get_user_model
|
||||
User = get_user_model()
|
||||
user = User.objects.filter(username=username).first()
|
||||
if user:
|
||||
print('Update existing user and set his password.', file=sys.stderr)
|
||||
user.is_active = True
|
||||
user.is_staff = True
|
||||
user.is_superuser = True
|
||||
user.set_password(password)
|
||||
user.email = email
|
||||
user.save()
|
||||
else:
|
||||
print('Create new super user', file=sys.stderr)
|
||||
User.objects.create_superuser(
|
||||
username=username,
|
||||
email=email,
|
||||
password=password
|
||||
)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
|
@ -3,6 +3,7 @@
|
|||
"""
|
||||
import multiprocessing
|
||||
|
||||
|
||||
bind = '127.0.0.1:__PORT__'
|
||||
|
||||
# https://docs.gunicorn.org/en/latest/settings.html#workers
|
||||
|
|
|
@ -7,6 +7,7 @@ import sys
|
|||
def main():
|
||||
os.environ['DJANGO_SETTINGS_MODULE'] = 'settings'
|
||||
from django.core.management import execute_from_command_line
|
||||
|
||||
execute_from_command_line(sys.argv)
|
||||
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
bleach==3.2.1; python_version >= "3.7" and python_full_version < "3.0.0" or python_version >= "3.7" and python_full_version < "4.0.0" and python_full_version >= "3.5.0" \
|
||||
--hash=sha256:9f8ccbeb6183c6e6cddea37592dfb0167485c1e3b13b3363bc325aa8bda3adbd \
|
||||
--hash=sha256:52b5919b81842b1854196eaae5ca29679a2f2e378905c346d3ca8227c2c66080
|
||||
bx-py-utils==18; python_version >= "3.7" and python_full_version < "4.0.0" \
|
||||
bx-py-utils==18; python_version >= "3.6" and python_full_version < "4.0.0" \
|
||||
--hash=sha256:72a6090822544603e3a7547ce07f0120ae67940ca2ec4590ac907b3b09ad70ca \
|
||||
--hash=sha256:195ea1b3d5d277354ea33e34ec3ebd4fc2a6e8d94d646ede902f80527f06ec75
|
||||
certifi==2020.12.5; python_version >= "3.7" and python_full_version < "3.0.0" or python_version >= "3.7" and python_full_version < "4.0.0" and python_full_version >= "3.5.0" \
|
||||
|
@ -61,9 +61,9 @@ django-tagulous==1.1.0; python_version >= "3.7" and python_full_version < "4.0.0
|
|||
django-tools==0.48.3; python_version >= "3.7" and python_full_version < "4.0.0" \
|
||||
--hash=sha256:08ed2ae606067f49c2c3949055227a826c8b880e5816114925eca386cf1823af \
|
||||
--hash=sha256:40444fa16b703b7c6960a800ba76aad42472c9aa70040d549a4d91dbb47a5ddb
|
||||
django-ynh==0.1.0; python_version >= "3.7" and python_full_version < "4.0.0" \
|
||||
--hash=sha256:db75d5d9f0b1744187168a1b815272cd8c86c9ecb0fd77c58517e82a2cd9b09b \
|
||||
--hash=sha256:955dc29f55c449e116876c7c920dc8451157bae2a246b1375822f5e47d4c6fd4
|
||||
django-ynh==0.1.2; python_version >= "3.7" and python_full_version < "4.0.0" \
|
||||
--hash=sha256:2efa30444f67252bbb7601e1b5631ce93ddf72a70b2216bb61363990de78ad4f \
|
||||
--hash=sha256:711b0f9ac183b2507a58cf644081aafefdc025ce585f6a8dad427ca3baf55a19
|
||||
django==2.2.17; python_version >= "3.7" and python_full_version < "4.0.0" and python_version < "4.0" \
|
||||
--hash=sha256:558cb27930defd9a6042133258caf797b2d1dee233959f537e3dc475cb49bd7c \
|
||||
--hash=sha256:cf5370a4d7765a9dd6d42a7b96b53c74f9446cd38209211304b210fe0404b861
|
||||
|
|
|
@ -11,9 +11,11 @@
|
|||
|
||||
from pathlib import Path as __Path
|
||||
|
||||
from django_ynh.secret_key import get_or_create_secret as __get_or_create_secret
|
||||
from inventory_project.settings.base import * # noqa
|
||||
|
||||
DEBUG = False
|
||||
|
||||
DEBUG = False # Don't turn DEBUG on in production!
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
|
@ -31,40 +33,41 @@ PATH_URL = PATH_URL.strip('/')
|
|||
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
ROOT_URLCONF = 'ynh_urls' # /opt/yunohost/pyinventory/ynh_urls.py
|
||||
ROOT_URLCONF = 'urls' # /opt/yunohost/pyinventory/ynh_urls.py
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Function that will be called to finalize a user profile:
|
||||
YNH_SETUP_USER = 'setup_user.setup_project_user'
|
||||
|
||||
SECRET_KEY = __get_or_create_secret(FINAL_HOME_PATH / 'secret.txt') # /opt/yunohost/$app/secret.txt
|
||||
|
||||
INSTALLED_APPS.append('django_ynh')
|
||||
|
||||
MIDDLEWARE.insert(
|
||||
MIDDLEWARE.index('django.contrib.auth.middleware.AuthenticationMiddleware') + 1,
|
||||
# login a user via HTTP_REMOTE_USER header from SSOwat:
|
||||
'django_ynh.sso_auth.auth_middleware.SSOwatRemoteUserMiddleware',
|
||||
)
|
||||
|
||||
# Keep ModelBackend around for per-user permissions and superuser
|
||||
AUTHENTICATION_BACKENDS = (
|
||||
'axes.backends.AxesBackend', # AxesBackend should be the first backend!
|
||||
|
||||
#
|
||||
# Authenticate via SSO and nginx 'HTTP_REMOTE_USER' header:
|
||||
'ynh_authenticate.RemoteUserBackend',
|
||||
|
||||
'django_ynh.sso_auth.auth_backend.SSOwatUserBackend',
|
||||
#
|
||||
# Fallback to normal Django model backend:
|
||||
'django.contrib.auth.backends.ModelBackend',
|
||||
)
|
||||
|
||||
LOGIN_REDIRECT_URL = None
|
||||
LOGIN_URL = '/yunohost/sso/'
|
||||
LOGOUT_REDIRECT_URL = '/yunohost/sso/'
|
||||
# /yunohost/sso/?action=logout
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# https://docs.djangoproject.com/en/2.2/howto/auth-remote-user/
|
||||
# Add RemoteUserMiddleware after AuthenticationMiddleware
|
||||
|
||||
MIDDLEWARE.insert(
|
||||
MIDDLEWARE.index('django.contrib.auth.middleware.AuthenticationMiddleware') + 1,
|
||||
'ynh_authenticate.RemoteUserMiddleware',
|
||||
)
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
|
||||
ADMINS = (
|
||||
('__ADMIN__', '__ADMINMAIL__'),
|
||||
)
|
||||
ADMINS = (('__ADMIN__', '__ADMINMAIL__'),)
|
||||
|
||||
MANAGERS = ADMINS
|
||||
|
||||
|
@ -170,6 +173,7 @@ LOGGING = {
|
|||
'django': {'handlers': ['log_file', 'mail_admins'], 'level': 'INFO', 'propagate': False},
|
||||
'axes': {'handlers': ['log_file', 'mail_admins'], 'level': 'WARNING', 'propagate': False},
|
||||
'django_tools': {'handlers': ['log_file', 'mail_admins'], 'level': 'INFO', 'propagate': False},
|
||||
'django_ynh': {'handlers': ['log_file', 'mail_admins'], 'level': 'INFO', 'propagate': False},
|
||||
'inventory': {'handlers': ['log_file', 'mail_admins'], 'level': 'INFO', 'propagate': False},
|
||||
},
|
||||
}
|
||||
|
|
8
conf/setup_user.py
Normal file
8
conf/setup_user.py
Normal file
|
@ -0,0 +1,8 @@
|
|||
def setup_project_user(user):
|
||||
"""
|
||||
All users used the Django admin, so we need to set the "staff" user flag.
|
||||
Called from django_ynh.sso_auth
|
||||
"""
|
||||
user.is_staff = True
|
||||
user.save()
|
||||
return user
|
|
@ -23,9 +23,7 @@ if settings.PATH_URL:
|
|||
urlpatterns = [
|
||||
# path(f'{settings.PATH_URL}/debug/', debug_view),
|
||||
path(f'{settings.PATH_URL}/', admin.site.urls),
|
||||
|
||||
path(f'{settings.PATH_URL}/ckeditor/', include('ckeditor_uploader.urls')),
|
||||
|
||||
# MEDIA_URL contains the "PATH_URL" already:
|
||||
path(settings.MEDIA_URL.lstrip('/'), include('django_tools.serve_media_app.urls')),
|
||||
]
|
||||
|
|
|
@ -2,7 +2,11 @@
|
|||
WSGI config
|
||||
"""
|
||||
import os
|
||||
|
||||
|
||||
os.environ['DJANGO_SETTINGS_MODULE'] = 'settings'
|
||||
|
||||
from django.core.wsgi import get_wsgi_application
|
||||
from django.core.wsgi import get_wsgi_application # noqa
|
||||
|
||||
|
||||
application = get_wsgi_application()
|
||||
|
|
|
@ -1,185 +0,0 @@
|
|||
"""
|
||||
* remote user authentication backend
|
||||
* remote user middleware
|
||||
|
||||
Note: SSOwat/nginx add authentication headers:
|
||||
|
||||
'HTTP_AUTHORIZATION': 'Basic XXXXXXXXXXXXXXXX='
|
||||
'HTTP_AUTH_USER': 'username'
|
||||
'HTTP_REMOTE_USER': 'username'
|
||||
|
||||
Basic auth contains "{username}:{plaintext-password}"
|
||||
|
||||
and we get SSOwat cookies like:
|
||||
|
||||
'HTTP_COOKIE': 'SSOwAuthUser=username; '
|
||||
'SSOwAuthHash=593876aa66...99e69f88af1e; '
|
||||
'SSOwAuthExpire=1609227697.998; '
|
||||
|
||||
* Login a user via HTTP_REMOTE_USER header, but check also username in:
|
||||
* SSOwAuthUser
|
||||
* HTTP_AUTH_USER
|
||||
* HTTP_AUTHORIZATION (Basic auth)
|
||||
* Create new users
|
||||
* Update Email, First / Last name for existing users
|
||||
"""
|
||||
import base64
|
||||
import logging
|
||||
|
||||
from axes.exceptions import AxesBackendPermissionDenied
|
||||
from django.contrib.auth.backends import RemoteUserBackend as OriginRemoteUserBackend
|
||||
from django.contrib.auth.middleware import RemoteUserMiddleware as OriginRemoteUserMiddleware
|
||||
from django.core.exceptions import ValidationError
|
||||
from inventory.permissions import get_or_create_normal_user_group
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def update_user_profile(request):
|
||||
"""
|
||||
Update existing user information:
|
||||
* Email
|
||||
* First / Last name
|
||||
"""
|
||||
user = request.user
|
||||
assert user.is_authenticated
|
||||
|
||||
update_fields = []
|
||||
|
||||
if not user.password:
|
||||
# Empty password is not valid, so we can't save the model, because of full_clean() call
|
||||
logger.info('Set unusable password for user: %s', user)
|
||||
user.set_unusable_password()
|
||||
update_fields.append('password')
|
||||
|
||||
email = request.META.get('HTTP_EMAIL')
|
||||
if email and user.email != email:
|
||||
logger.info('Update email: %r -> %r', user.email, email)
|
||||
user.email = email
|
||||
update_fields.append('email')
|
||||
|
||||
raw_username = request.META.get('HTTP_NAME')
|
||||
if raw_username:
|
||||
if ' ' in raw_username:
|
||||
first_name, last_name = raw_username.split(' ', 1)
|
||||
else:
|
||||
first_name = ''
|
||||
last_name = raw_username
|
||||
|
||||
if user.first_name != first_name:
|
||||
logger.info('Update first name: %r -> %r', user.first_name, first_name)
|
||||
user.first_name = first_name
|
||||
update_fields.append('first_name')
|
||||
|
||||
if user.last_name != last_name:
|
||||
logger.info('Update last name: %r -> %r', user.last_name, last_name)
|
||||
user.last_name = last_name
|
||||
update_fields.append('last_name')
|
||||
|
||||
if update_fields:
|
||||
try:
|
||||
user.full_clean()
|
||||
except ValidationError:
|
||||
logger.exception('Can not update user: %s', user)
|
||||
else:
|
||||
user.save(update_fields=update_fields)
|
||||
|
||||
|
||||
class RemoteUserMiddleware(OriginRemoteUserMiddleware):
|
||||
"""
|
||||
Middleware to login a user HTTP_REMOTE_USER header.
|
||||
Use Django Axes if something is wrong.
|
||||
Update exising user informations.
|
||||
"""
|
||||
header = 'HTTP_REMOTE_USER'
|
||||
force_logout_if_no_header = True
|
||||
|
||||
def process_request(self, request):
|
||||
# Keep the information if the user is already logged in
|
||||
was_authenticated = request.user.is_authenticated
|
||||
|
||||
super().process_request(request) # login remote user
|
||||
|
||||
if not request.user.is_authenticated:
|
||||
# Not logged in -> nothing to verify here
|
||||
return
|
||||
|
||||
# Check SSOwat cookie informations:
|
||||
try:
|
||||
username = request.COOKIES['SSOwAuthUser']
|
||||
except KeyError:
|
||||
logger.error('SSOwAuthUser cookie missing!')
|
||||
|
||||
# emits a signal indicating user login failed, which is processed by
|
||||
# axes.signals.log_user_login_failed which logs and flags the failed request.
|
||||
raise AxesBackendPermissionDenied('Cookie missing')
|
||||
|
||||
logger.info('SSOwat username from cookies: %r', username)
|
||||
if username != request.user.username:
|
||||
raise AxesBackendPermissionDenied('Wrong username')
|
||||
|
||||
# Compare with HTTP_AUTH_USER
|
||||
try:
|
||||
username = request.META['HTTP_AUTH_USER']
|
||||
except KeyError:
|
||||
logger.error('HTTP_AUTH_USER missing!')
|
||||
raise AxesBackendPermissionDenied('No HTTP_AUTH_USER')
|
||||
|
||||
if username != request.user.username:
|
||||
raise AxesBackendPermissionDenied('Wrong HTTP_AUTH_USER username')
|
||||
|
||||
# Also check 'HTTP_AUTHORIZATION', but only the username ;)
|
||||
try:
|
||||
auth = request.META['HTTP_AUTHORIZATION']
|
||||
except KeyError:
|
||||
logger.error('HTTP_AUTHORIZATION missing!')
|
||||
raise AxesBackendPermissionDenied('No HTTP_AUTHORIZATION')
|
||||
|
||||
scheme, creds = auth.split(' ', 1)
|
||||
if scheme.lower() != 'basic':
|
||||
logger.error('HTTP_AUTHORIZATION with %r not supported', scheme)
|
||||
raise AxesBackendPermissionDenied('HTTP_AUTHORIZATION scheme not supported')
|
||||
|
||||
creds = str(base64.b64decode(creds), encoding='utf-8')
|
||||
username = creds.split(':', 1)[0]
|
||||
if username != request.user.username:
|
||||
raise AxesBackendPermissionDenied('Wrong HTTP_AUTHORIZATION username')
|
||||
|
||||
if not was_authenticated:
|
||||
# First request, after login -> update user informations
|
||||
logger.info('Remote used was logged in')
|
||||
update_user_profile(request)
|
||||
|
||||
|
||||
class RemoteUserBackend(OriginRemoteUserBackend):
|
||||
"""
|
||||
Authentication backend via SSO/nginx header
|
||||
"""
|
||||
create_unknown_user = True
|
||||
|
||||
def authenticate(self, request, remote_user):
|
||||
logger.info('Remote user authenticate: %r', remote_user)
|
||||
return super().authenticate(request, remote_user)
|
||||
|
||||
def configure_user(self, request, user):
|
||||
"""
|
||||
Configure a user after creation and return the updated user.
|
||||
Setup a normal, non-superuser
|
||||
"""
|
||||
logger.warning('Configure user %s', user)
|
||||
|
||||
user.set_unusable_password() # Always login via SSO
|
||||
user.is_staff = True
|
||||
user.is_superuser = False
|
||||
user.save()
|
||||
|
||||
pyinventory_user_group = get_or_create_normal_user_group()[0]
|
||||
user.groups.set([pyinventory_user_group])
|
||||
|
||||
update_user_profile(request)
|
||||
|
||||
return user
|
||||
|
||||
def user_can_authenticate(self, user):
|
||||
logger.warning('Remote user login: %s', user)
|
||||
return True
|
149
local_test.py
Executable file → Normal file
149
local_test.py
Executable file → Normal file
|
@ -1,154 +1,29 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
"""
|
||||
Start PyInventory in YunoHost setup locally.
|
||||
Note:
|
||||
You can only run this script, if you are in a activated PyInventory venv!
|
||||
Build a "local_test" YunoHost installation and start the Django dev. server against it.
|
||||
|
||||
Run via:
|
||||
make local-test
|
||||
|
||||
see README for details ;)
|
||||
"""
|
||||
|
||||
import os
|
||||
import shlex
|
||||
import subprocess
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
os.environ['DJANGO_SETTINGS_MODULE'] = 'ynh_pyinventory_settings'
|
||||
|
||||
try:
|
||||
import inventory_project # noqa
|
||||
from django_ynh.local_test import create_local_test
|
||||
except ImportError as err:
|
||||
raise ImportError(
|
||||
'Couldn\'t import PyInventory. Did you '
|
||||
'forget to activate a virtual environment?'
|
||||
) from err
|
||||
raise ImportError('Did you forget to activate a virtual environment?') from err
|
||||
|
||||
|
||||
BASE_PATH = Path(__file__).parent.absolute()
|
||||
TEST_PATH = BASE_PATH / 'local_test'
|
||||
CONF_PATH = BASE_PATH / 'conf'
|
||||
|
||||
FINAL_HOME_PATH = TEST_PATH / 'opt_yunohost'
|
||||
FINAL_WWW_PATH = TEST_PATH / 'var_www'
|
||||
LOG_FILE = TEST_PATH / 'var_log_pyinventory.log'
|
||||
|
||||
MANAGE_PY_FILE = CONF_PATH / 'manage.py'
|
||||
CREATE_SUPERUSER_FILE = CONF_PATH / 'create_superuser.py'
|
||||
SETTINGS_FILE = CONF_PATH / 'ynh_pyinventory_settings.py'
|
||||
URLS_FILE = CONF_PATH / 'ynh_urls.py'
|
||||
|
||||
REPLACES = {
|
||||
'__FINAL_HOME_PATH__': str(FINAL_HOME_PATH),
|
||||
'__FINAL_WWW_PATH__': str(FINAL_WWW_PATH),
|
||||
'__LOG_FILE__': str(TEST_PATH / 'var_log_pyinventory.log'),
|
||||
|
||||
'__PATH_URL__': 'app_path',
|
||||
'__DOMAIN__': '127.0.0.1',
|
||||
|
||||
'django.db.backends.postgresql': 'django.db.backends.sqlite3',
|
||||
"'NAME': '__APP__',": f"'NAME': '{TEST_PATH / 'test_db.sqlite'}',",
|
||||
|
||||
'django_redis.cache.RedisCache': 'django.core.cache.backends.dummy.DummyCache',
|
||||
|
||||
'DEBUG = False': 'DEBUG = True',
|
||||
|
||||
# Just use the default logging setup from PyInventory project:
|
||||
'LOGGING = {': 'HACKED_DEACTIVATED_LOGGING = {',
|
||||
}
|
||||
|
||||
|
||||
def verbose_check_call(command, verbose=True, **kwargs):
|
||||
""" 'verbose' version of subprocess.check_call() """
|
||||
if verbose:
|
||||
print('_' * 100)
|
||||
msg = f'Call: {command!r}'
|
||||
verbose_kwargs = ', '.join(f'{k}={v!r}' for k, v in sorted(kwargs.items()))
|
||||
if verbose_kwargs:
|
||||
msg += f' (kwargs: {verbose_kwargs})'
|
||||
print(f'{msg}\n', flush=True)
|
||||
|
||||
env = os.environ.copy()
|
||||
env['PYTHONUNBUFFERED'] = '1'
|
||||
|
||||
popenargs = shlex.split(command)
|
||||
subprocess.check_call(
|
||||
popenargs,
|
||||
universal_newlines=True,
|
||||
env=env,
|
||||
**kwargs
|
||||
)
|
||||
|
||||
|
||||
def call_manage_py(args):
|
||||
verbose_check_call(
|
||||
command=f'{sys.executable} manage.py {args}',
|
||||
cwd=FINAL_HOME_PATH,
|
||||
)
|
||||
|
||||
|
||||
def copy_patch(src_file, replaces=None):
|
||||
dst_file = FINAL_HOME_PATH / src_file.name
|
||||
print(f'{src_file.relative_to(BASE_PATH)} -> {dst_file.relative_to(BASE_PATH)}')
|
||||
|
||||
with src_file.open('r') as f:
|
||||
content = f.read()
|
||||
|
||||
if replaces:
|
||||
for old, new in replaces.items():
|
||||
content = content.replace(old, new)
|
||||
|
||||
with dst_file.open('w') as f:
|
||||
f.write(content)
|
||||
BASE_PATH = Path(__file__).parent
|
||||
|
||||
|
||||
def main():
|
||||
print('-' * 100)
|
||||
|
||||
assert BASE_PATH.is_dir()
|
||||
assert CONF_PATH.is_dir()
|
||||
assert SETTINGS_FILE.is_file()
|
||||
assert URLS_FILE.is_file()
|
||||
|
||||
for p in (TEST_PATH, FINAL_HOME_PATH, FINAL_WWW_PATH):
|
||||
if p.is_dir():
|
||||
print(f'Already exists: "{p.relative_to(BASE_PATH)}", ok.')
|
||||
else:
|
||||
print(f'Create: "{p.relative_to(BASE_PATH)}"')
|
||||
p.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
LOG_FILE.touch(exist_ok=True)
|
||||
|
||||
# conf/manage.py -> local_test/manage.py
|
||||
copy_patch(src_file=MANAGE_PY_FILE)
|
||||
|
||||
# conf/create_superuser.py -> local_test/opt_yunohost/create_superuser.py
|
||||
copy_patch(src_file=CREATE_SUPERUSER_FILE)
|
||||
|
||||
# conf/ynh_pyinventory_settings.py -> local_test/ynh_pyinventory_settings.py
|
||||
copy_patch(src_file=SETTINGS_FILE, replaces=REPLACES)
|
||||
|
||||
# conf/ynh_urls.py -> local_test/ynh_urls.py
|
||||
copy_patch(src_file=URLS_FILE, replaces=REPLACES)
|
||||
|
||||
with Path(FINAL_HOME_PATH / 'local_settings.py').open('w') as f:
|
||||
f.write('# Only for local test run\n')
|
||||
f.write('SERVE_FILES=True # used in src/inventory_project/urls.py\n')
|
||||
|
||||
# call "local_test/manage.py" via subprocess:
|
||||
call_manage_py('check --deploy')
|
||||
call_manage_py('migrate --no-input')
|
||||
call_manage_py('collectstatic --no-input')
|
||||
|
||||
verbose_check_call(
|
||||
command=f'{sys.executable} create_superuser.py --username="test" --password="test"',
|
||||
cwd=FINAL_HOME_PATH,
|
||||
create_local_test(
|
||||
django_settings_path=BASE_PATH / 'conf' / 'settings.py',
|
||||
destination=BASE_PATH / 'local_test',
|
||||
runserver=True,
|
||||
)
|
||||
|
||||
try:
|
||||
call_manage_py('runserver --nostatic')
|
||||
except KeyboardInterrupt:
|
||||
print('\nBye ;)')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
"description": {
|
||||
"en": "Web based management to catalog things including state and location etc."
|
||||
},
|
||||
"version": "0.8.2~ynh4",
|
||||
"version": "0.8.2~ynh5",
|
||||
"url": "https://github.com/jedie/PyInventory",
|
||||
"license": "GPL-3.0",
|
||||
"maintainer": {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[tool.poetry]
|
||||
name = "pyinventory_ynh"
|
||||
version = "0.8.2~ynh4"
|
||||
version = "0.8.2~ynh5"
|
||||
description = "Test pyinventory_ynh via local_test.py"
|
||||
authors = ["JensDiemer <git@jensdiemer.de>"]
|
||||
license = "GPL"
|
||||
|
@ -11,7 +11,72 @@ pyinventory = "*"
|
|||
django_ynh = "*"
|
||||
|
||||
[tool.poetry.dev-dependencies]
|
||||
bx_py_utils = "*"
|
||||
isort = "*"
|
||||
flake8 = "*"
|
||||
flynt = "*"
|
||||
black = "*"
|
||||
pyupgrade = "*"
|
||||
|
||||
[build-system]
|
||||
requires = ["poetry-core>=1.0.0"]
|
||||
build-backend = "poetry.core.masonry.api"
|
||||
|
||||
|
||||
[tool.isort]
|
||||
# https://pycqa.github.io/isort/docs/configuration/config_files/#pyprojecttoml-preferred-format
|
||||
atomic=true
|
||||
line_length=120
|
||||
case_sensitive=false
|
||||
skip_glob=["*/htmlcov/*","*/migrations/*"]
|
||||
multi_line_output=3
|
||||
include_trailing_comma=true
|
||||
known_first_party=["inventory"]
|
||||
no_lines_before="LOCALFOLDER"
|
||||
default_section="THIRDPARTY"
|
||||
sections=["FUTURE","STDLIB","THIRDPARTY","FIRSTPARTY","LOCALFOLDER"]
|
||||
lines_after_imports=2
|
||||
|
||||
|
||||
[tool.pytest.ini_options]
|
||||
# https://docs.pytest.org/en/latest/customize.html#pyproject-toml
|
||||
minversion = "6.0"
|
||||
norecursedirs = ".* .git __pycache__ conf coverage* dist htmlcov"
|
||||
# sometimes helpfull "addopts" arguments:
|
||||
# -vv
|
||||
# --verbose
|
||||
# --capture=no
|
||||
# --trace-config
|
||||
# --full-trace
|
||||
# -p no:warnings
|
||||
addopts = """
|
||||
--import-mode=importlib
|
||||
--reuse-db
|
||||
--nomigrations
|
||||
--cov=.
|
||||
--cov-report term-missing
|
||||
--cov-report html
|
||||
--cov-report xml
|
||||
--no-cov-on-fail
|
||||
--showlocals
|
||||
--doctest-modules
|
||||
--failed-first
|
||||
--last-failed-no-failures all
|
||||
--new-first
|
||||
"""
|
||||
|
||||
|
||||
[tool.tox]
|
||||
# https://tox.readthedocs.io/en/latest/example/basic.html#pyproject-toml-tox-legacy-ini
|
||||
legacy_tox_ini = """
|
||||
[tox]
|
||||
isolated_build = True
|
||||
envlist = py39,py38,py37
|
||||
skip_missing_interpreters = True
|
||||
|
||||
[testenv]
|
||||
passenv = *
|
||||
whitelist_externals = make
|
||||
commands =
|
||||
make pytest
|
||||
"""
|
||||
|
|
|
@ -27,9 +27,6 @@ log_file="${log_path}/pyinventory.log"
|
|||
# dependencies used by the app
|
||||
pkg_dependencies="build-essential python3-dev python3-pip python3-venv git postgresql postgresql-contrib"
|
||||
|
||||
# PyInventory's version for PIP and settings file
|
||||
pyinventory_version="0.8.2"
|
||||
|
||||
#=================================================
|
||||
# Redis HELPERS
|
||||
#=================================================
|
||||
|
|
|
@ -116,10 +116,10 @@ fi
|
|||
ynh_script_progression --message="Modify PyInventory's config file..."
|
||||
|
||||
# save old settings file
|
||||
settings="$final_path/ynh_pyinventory_settings.py"
|
||||
settings="$final_path/settings.py"
|
||||
ynh_backup_if_checksum_is_different --file="$settings"
|
||||
|
||||
cp "../conf/ynh_pyinventory_settings.py" "$settings"
|
||||
cp "../conf/settings.py" "$settings"
|
||||
|
||||
ynh_replace_string --match_string="__APP__" --replace_string="$app" --target_file="$settings"
|
||||
ynh_replace_string --match_string="__DB_PWD__" --replace_string="$db_pwd" --target_file="$settings"
|
||||
|
|
|
@ -101,7 +101,7 @@ ynh_system_user_create --username="$app" --home_dir="$final_path" --use_shell
|
|||
#=================================================
|
||||
# PIP INSTALLATION
|
||||
#=================================================
|
||||
ynh_script_progression --message="Install PyInventory using PIP..." --weight=80
|
||||
ynh_script_progression --message="Install project via pip..." --weight=80
|
||||
|
||||
python3 -m venv "${final_path}/venv"
|
||||
cp ../conf/requirements.txt "$final_path/requirements.txt"
|
||||
|
@ -119,10 +119,7 @@ chown -R "$app" "$final_path"
|
|||
#=================================================
|
||||
# copy config files
|
||||
# ================================================
|
||||
ynh_script_progression --message="Create pyinventory configuration file..."
|
||||
|
||||
cp ../conf/create_superuser.py "$final_path/create_superuser.py"
|
||||
chmod +x "$final_path/create_superuser.py"
|
||||
ynh_script_progression --message="Create project configuration files..."
|
||||
|
||||
gunicorn_conf="$final_path/gunicorn.conf.py"
|
||||
cp "../conf/gunicorn.conf.py" "$gunicorn_conf"
|
||||
|
@ -134,10 +131,8 @@ ynh_store_file_checksum --file="$gunicorn_conf"
|
|||
cp ../conf/manage.py "$final_path/manage.py"
|
||||
chmod +x "$final_path/manage.py"
|
||||
|
||||
cp ../conf/wsgi.py "$final_path/wsgi.py"
|
||||
|
||||
settings="$final_path/ynh_pyinventory_settings.py"
|
||||
cp "../conf/ynh_pyinventory_settings.py" "$settings"
|
||||
settings="$final_path/settings.py"
|
||||
cp "../conf/settings.py" "$settings"
|
||||
|
||||
ynh_replace_string --match_string="__APP__" --replace_string="$app" --target_file="$settings"
|
||||
ynh_replace_string --match_string="__DB_PWD__" --replace_string="$db_pwd" --target_file="$settings"
|
||||
|
@ -156,10 +151,11 @@ ynh_store_file_checksum --file="$settings"
|
|||
|
||||
ynh_app_setting_set --app="$app" --key=redis_db --value="$redis_db"
|
||||
|
||||
touch "$final_path/local_settings.py"
|
||||
cp ../conf/setup_user.py "$final_path/setup_user.py"
|
||||
cp ../conf/urls.py "$final_path/urls.py"
|
||||
cp ../conf/wsgi.py "$final_path/wsgi.py"
|
||||
|
||||
cp "../conf/ynh_authenticate.py" "$final_path/ynh_authenticate.py"
|
||||
cp "../conf/ynh_urls.py" "$final_path/ynh_urls.py"
|
||||
touch "$final_path/local_settings.py"
|
||||
|
||||
#=================================================
|
||||
# MIGRATE / COLLECTSTATIC / CREATEADMIN
|
||||
|
@ -177,7 +173,9 @@ ynh_script_progression --message="migrate/collectstatic/createadmin..." --weight
|
|||
|
||||
./manage.py migrate --no-input
|
||||
./manage.py collectstatic --no-input
|
||||
./create_superuser.py --username="$admin" --email="$admin_mail" --password="pyinventory"
|
||||
|
||||
# Create/update Django superuser (set unusable password, because auth done via SSOwat):
|
||||
./manage.py create_superuser --username="$admin" --email="$admin_mail"
|
||||
|
||||
# Check the configuration
|
||||
# This may fail in some cases with errors, etc., but the app works and the user can fix issues later.
|
||||
|
|
|
@ -83,10 +83,9 @@ ynh_script_progression --message="Configuring a systemd service..."
|
|||
ynh_add_systemd_config --service="$app" --template="pyinventory.service"
|
||||
|
||||
#=================================================
|
||||
# UPGRADE PYINVENTORY
|
||||
# UPGRADE VENV
|
||||
#=================================================
|
||||
|
||||
ynh_script_progression --message="Install pyinventory using PIP..." --weight=15
|
||||
ynh_script_progression --message="Upgrade project via pip..." --weight=80
|
||||
|
||||
python3 -m venv "${final_path}/venv"
|
||||
cp ../conf/requirements.txt "$final_path/requirements.txt"
|
||||
|
@ -94,21 +93,17 @@ chown -R "$app" "$final_path"
|
|||
|
||||
#run source in a 'sub shell'
|
||||
(
|
||||
set +o nounset
|
||||
source "${final_path}/venv/bin/activate"
|
||||
set -o nounset
|
||||
ynh_exec_as $app $final_path/venv/bin/pip install --upgrade pip
|
||||
ynh_exec_as $app $final_path/venv/bin/pip install -r "$final_path/requirements.txt"
|
||||
set +o nounset
|
||||
source "${final_path}/venv/bin/activate"
|
||||
set -o nounset
|
||||
ynh_exec_as $app $final_path/venv/bin/pip install --upgrade pip
|
||||
ynh_exec_as $app $final_path/venv/bin/pip install -r "$final_path/requirements.txt"
|
||||
)
|
||||
|
||||
#=================================================
|
||||
# copy config files
|
||||
# ================================================
|
||||
ynh_script_progression --message="Create pyinventory configuration file..."
|
||||
|
||||
ynh_backup_if_checksum_is_different --file="$final_path/create_superuser.py"
|
||||
cp ../conf/create_superuser.py "$final_path/create_superuser.py"
|
||||
chmod +x "$final_path/create_superuser.py"
|
||||
ynh_script_progression --message="Create project configuration files..."
|
||||
|
||||
gunicorn_conf="$final_path/gunicorn.conf.py"
|
||||
ynh_backup_if_checksum_is_different --file="$gunicorn_conf"
|
||||
|
@ -122,14 +117,11 @@ ynh_backup_if_checksum_is_different --file="$final_path/manage.py"
|
|||
cp ../conf/manage.py "$final_path/manage.py"
|
||||
chmod +x "$final_path/manage.py"
|
||||
|
||||
ynh_backup_if_checksum_is_different --file="$final_path/wsgi.py"
|
||||
cp ../conf/wsgi.py "$final_path/wsgi.py"
|
||||
|
||||
# save old settings file
|
||||
settings="$final_path/ynh_pyinventory_settings.py"
|
||||
settings="$final_path/settings.py"
|
||||
ynh_backup_if_checksum_is_different --file="$settings"
|
||||
|
||||
cp "../conf/ynh_pyinventory_settings.py" "$settings"
|
||||
cp "../conf/settings.py" "$settings"
|
||||
|
||||
ynh_replace_string --match_string="__APP__" --replace_string="$app" --target_file="$settings"
|
||||
ynh_replace_string --match_string="__DB_PWD__" --replace_string="$db_pwd" --target_file="$settings"
|
||||
|
@ -146,10 +138,16 @@ ynh_replace_string --match_string="__PATH_URL__" --replace_string="$path_url" --
|
|||
# Recalculate and store the config file checksum into the app settings
|
||||
ynh_store_file_checksum --file="$settings"
|
||||
|
||||
touch "$final_path/local_settings.py"
|
||||
ynh_backup_if_checksum_is_different --file="$final_path/setup_user.py"
|
||||
cp ../conf/setup_user.py "$final_path/setup_user.py"
|
||||
|
||||
cp "../conf/ynh_authenticate.py" "$final_path/ynh_authenticate.py"
|
||||
cp "../conf/ynh_urls.py" "$final_path/ynh_urls.py"
|
||||
ynh_backup_if_checksum_is_different --file="$final_path/urls.py"
|
||||
cp ../conf/urls.py "$final_path/urls.py"
|
||||
|
||||
ynh_backup_if_checksum_is_different --file="$final_path/wsgi.py"
|
||||
cp ../conf/wsgi.py "$final_path/wsgi.py"
|
||||
|
||||
touch "$final_path/local_settings.py"
|
||||
|
||||
#=================================================
|
||||
# MIGRATE PYINVENTORY
|
||||
|
@ -167,7 +165,9 @@ ynh_script_progression --message="migrate/collectstatic/createadmin..." --weight
|
|||
|
||||
./manage.py migrate --no-input
|
||||
./manage.py collectstatic --no-input
|
||||
./create_superuser.py --username="$admin" --email="$admin_mail" --password="pyinventory"
|
||||
|
||||
# Create/update Django superuser (set unusable password, because auth done via SSOwat):
|
||||
./manage.py create_superuser --username="$admin" --email="$admin_mail"
|
||||
|
||||
# Check the configuration
|
||||
# This may fail in some cases with errors, etc., but the app works and the user can fix issues later.
|
||||
|
|
Loading…
Reference in a new issue