1
0
Fork 0
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:
JensDiemer 2020-12-29 13:58:40 +01:00
parent cca6eef925
commit add1026c11
19 changed files with 173 additions and 440 deletions

2
.gitignore vendored
View file

@ -6,3 +6,5 @@ __pycache__
secret.txt secret.txt
/local_test/ /local_test/
/poetry.lock /poetry.lock
/coverage.xml
/htmlcov/

View file

@ -1,4 +1,5 @@
SHELL := /bin/bash SHELL := /bin/bash
MAX_LINE_LENGTH := 119
all: help all: help
@ -27,8 +28,18 @@ update: install-poetry ## update the sources and installation and generate "con
poetry update poetry update
poetry export -f requirements.txt --output conf/requirements.txt poetry export -f requirements.txt --output conf/requirements.txt
local-test: check-poetry ## Run local_test.py to run the project locally lint: ## Run code formatters and linter
poetry run ./local_test.py 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 local-diff-settings: ## Run "manage.py diffsettings" with local test
poetry run python3 local_test/opt_yunohost/manage.py diffsettings poetry run python3 local_test/opt_yunohost/manage.py diffsettings

View file

@ -21,7 +21,7 @@ Pull requests welcome ;)
## Settings and upgrades ## 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. You can edit the file `$final_path/local_settings.py` to enable or disable features.
# Miscellaneous # 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 -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 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 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:~# cd /opt/yunohost/pyinventory/
root@yunohost:/opt/yunohost/pyinventory# source venv/bin/activate root@yunohost:/opt/yunohost/pyinventory# source venv/bin/activate
(venv) root@yunohost:/opt/yunohost/pyinventory# ./manage.py check (venv) root@yunohost:/opt/yunohost/pyinventory# ./manage.py check
PyInventory v0.8.2 (Django v2.2.17) 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 PROJECT_PATH:/opt/yunohost/pyinventory/venv/lib/python3.7/site-packages
BASE_PATH:/opt/yunohost/pyinventory BASE_PATH:/opt/yunohost/pyinventory
System check identified no issues (0 silenced). System check identified no issues (0 silenced).

View file

@ -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()

View file

@ -3,6 +3,7 @@
""" """
import multiprocessing import multiprocessing
bind = '127.0.0.1:__PORT__' bind = '127.0.0.1:__PORT__'
# https://docs.gunicorn.org/en/latest/settings.html#workers # https://docs.gunicorn.org/en/latest/settings.html#workers

View file

@ -7,6 +7,7 @@ import sys
def main(): def main():
os.environ['DJANGO_SETTINGS_MODULE'] = 'settings' os.environ['DJANGO_SETTINGS_MODULE'] = 'settings'
from django.core.management import execute_from_command_line from django.core.management import execute_from_command_line
execute_from_command_line(sys.argv) execute_from_command_line(sys.argv)

View file

@ -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" \ 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:9f8ccbeb6183c6e6cddea37592dfb0167485c1e3b13b3363bc325aa8bda3adbd \
--hash=sha256:52b5919b81842b1854196eaae5ca29679a2f2e378905c346d3ca8227c2c66080 --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:72a6090822544603e3a7547ce07f0120ae67940ca2ec4590ac907b3b09ad70ca \
--hash=sha256:195ea1b3d5d277354ea33e34ec3ebd4fc2a6e8d94d646ede902f80527f06ec75 --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" \ 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" \ django-tools==0.48.3; python_version >= "3.7" and python_full_version < "4.0.0" \
--hash=sha256:08ed2ae606067f49c2c3949055227a826c8b880e5816114925eca386cf1823af \ --hash=sha256:08ed2ae606067f49c2c3949055227a826c8b880e5816114925eca386cf1823af \
--hash=sha256:40444fa16b703b7c6960a800ba76aad42472c9aa70040d549a4d91dbb47a5ddb --hash=sha256:40444fa16b703b7c6960a800ba76aad42472c9aa70040d549a4d91dbb47a5ddb
django-ynh==0.1.0; python_version >= "3.7" and python_full_version < "4.0.0" \ django-ynh==0.1.2; python_version >= "3.7" and python_full_version < "4.0.0" \
--hash=sha256:db75d5d9f0b1744187168a1b815272cd8c86c9ecb0fd77c58517e82a2cd9b09b \ --hash=sha256:2efa30444f67252bbb7601e1b5631ce93ddf72a70b2216bb61363990de78ad4f \
--hash=sha256:955dc29f55c449e116876c7c920dc8451157bae2a246b1375822f5e47d4c6fd4 --hash=sha256:711b0f9ac183b2507a58cf644081aafefdc025ce585f6a8dad427ca3baf55a19
django==2.2.17; python_version >= "3.7" and python_full_version < "4.0.0" and python_version < "4.0" \ 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:558cb27930defd9a6042133258caf797b2d1dee233959f537e3dc475cb49bd7c \
--hash=sha256:cf5370a4d7765a9dd6d42a7b96b53c74f9446cd38209211304b210fe0404b861 --hash=sha256:cf5370a4d7765a9dd6d42a7b96b53c74f9446cd38209211304b210fe0404b861

View file

@ -11,9 +11,11 @@
from pathlib import Path as __Path 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 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 # Keep ModelBackend around for per-user permissions and superuser
AUTHENTICATION_BACKENDS = ( AUTHENTICATION_BACKENDS = (
'axes.backends.AxesBackend', # AxesBackend should be the first backend! 'axes.backends.AxesBackend', # AxesBackend should be the first backend!
#
# Authenticate via SSO and nginx 'HTTP_REMOTE_USER' header: # 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: # Fallback to normal Django model backend:
'django.contrib.auth.backends.ModelBackend', 'django.contrib.auth.backends.ModelBackend',
) )
LOGIN_REDIRECT_URL = None LOGIN_REDIRECT_URL = None
LOGIN_URL = '/yunohost/sso/' LOGIN_URL = '/yunohost/sso/'
LOGOUT_REDIRECT_URL = '/yunohost/sso/' LOGOUT_REDIRECT_URL = '/yunohost/sso/'
# /yunohost/sso/?action=logout # /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 = ( ADMINS = (('__ADMIN__', '__ADMINMAIL__'),)
('__ADMIN__', '__ADMINMAIL__'),
)
MANAGERS = ADMINS MANAGERS = ADMINS
@ -170,6 +173,7 @@ LOGGING = {
'django': {'handlers': ['log_file', 'mail_admins'], 'level': 'INFO', 'propagate': False}, 'django': {'handlers': ['log_file', 'mail_admins'], 'level': 'INFO', 'propagate': False},
'axes': {'handlers': ['log_file', 'mail_admins'], 'level': 'WARNING', 'propagate': False}, 'axes': {'handlers': ['log_file', 'mail_admins'], 'level': 'WARNING', 'propagate': False},
'django_tools': {'handlers': ['log_file', 'mail_admins'], 'level': 'INFO', '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}, 'inventory': {'handlers': ['log_file', 'mail_admins'], 'level': 'INFO', 'propagate': False},
}, },
} }

8
conf/setup_user.py Normal file
View 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

View file

@ -23,9 +23,7 @@ if settings.PATH_URL:
urlpatterns = [ urlpatterns = [
# path(f'{settings.PATH_URL}/debug/', debug_view), # path(f'{settings.PATH_URL}/debug/', debug_view),
path(f'{settings.PATH_URL}/', admin.site.urls), path(f'{settings.PATH_URL}/', admin.site.urls),
path(f'{settings.PATH_URL}/ckeditor/', include('ckeditor_uploader.urls')), path(f'{settings.PATH_URL}/ckeditor/', include('ckeditor_uploader.urls')),
# MEDIA_URL contains the "PATH_URL" already: # MEDIA_URL contains the "PATH_URL" already:
path(settings.MEDIA_URL.lstrip('/'), include('django_tools.serve_media_app.urls')), path(settings.MEDIA_URL.lstrip('/'), include('django_tools.serve_media_app.urls')),
] ]

View file

@ -2,7 +2,11 @@
WSGI config WSGI config
""" """
import os import os
os.environ['DJANGO_SETTINGS_MODULE'] = 'settings' 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() application = get_wsgi_application()

View file

@ -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
View file

@ -1,154 +1,29 @@
#!/usr/bin/env python3
""" """
Start PyInventory in YunoHost setup locally. Build a "local_test" YunoHost installation and start the Django dev. server against it.
Note:
You can only run this script, if you are in a activated PyInventory venv! Run via:
make local-test
see README for details ;) see README for details ;)
""" """
import os
import shlex
import subprocess
import sys
from pathlib import Path from pathlib import Path
os.environ['DJANGO_SETTINGS_MODULE'] = 'ynh_pyinventory_settings'
try: try:
import inventory_project # noqa from django_ynh.local_test import create_local_test
except ImportError as err: except ImportError as err:
raise ImportError( raise ImportError('Did you forget to activate a virtual environment?') from err
'Couldn\'t import PyInventory. Did you '
'forget to activate a virtual environment?'
) from err
BASE_PATH = Path(__file__).parent
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)
def main(): def main():
print('-' * 100) create_local_test(
django_settings_path=BASE_PATH / 'conf' / 'settings.py',
assert BASE_PATH.is_dir() destination=BASE_PATH / 'local_test',
assert CONF_PATH.is_dir() runserver=True,
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,
) )
try:
call_manage_py('runserver --nostatic')
except KeyboardInterrupt:
print('\nBye ;)')
if __name__ == '__main__': if __name__ == '__main__':
main() main()

View file

@ -5,7 +5,7 @@
"description": { "description": {
"en": "Web based management to catalog things including state and location etc." "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", "url": "https://github.com/jedie/PyInventory",
"license": "GPL-3.0", "license": "GPL-3.0",
"maintainer": { "maintainer": {

View file

@ -1,6 +1,6 @@
[tool.poetry] [tool.poetry]
name = "pyinventory_ynh" name = "pyinventory_ynh"
version = "0.8.2~ynh4" version = "0.8.2~ynh5"
description = "Test pyinventory_ynh via local_test.py" description = "Test pyinventory_ynh via local_test.py"
authors = ["JensDiemer <git@jensdiemer.de>"] authors = ["JensDiemer <git@jensdiemer.de>"]
license = "GPL" license = "GPL"
@ -11,7 +11,72 @@ pyinventory = "*"
django_ynh = "*" django_ynh = "*"
[tool.poetry.dev-dependencies] [tool.poetry.dev-dependencies]
bx_py_utils = "*"
isort = "*"
flake8 = "*"
flynt = "*"
black = "*"
pyupgrade = "*"
[build-system] [build-system]
requires = ["poetry-core>=1.0.0"] requires = ["poetry-core>=1.0.0"]
build-backend = "poetry.core.masonry.api" 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
"""

View file

@ -27,9 +27,6 @@ log_file="${log_path}/pyinventory.log"
# dependencies used by the app # dependencies used by the app
pkg_dependencies="build-essential python3-dev python3-pip python3-venv git postgresql postgresql-contrib" 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 # Redis HELPERS
#================================================= #=================================================

View file

@ -116,10 +116,10 @@ fi
ynh_script_progression --message="Modify PyInventory's config file..." ynh_script_progression --message="Modify PyInventory's config file..."
# save old settings 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" 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="__APP__" --replace_string="$app" --target_file="$settings"
ynh_replace_string --match_string="__DB_PWD__" --replace_string="$db_pwd" --target_file="$settings" ynh_replace_string --match_string="__DB_PWD__" --replace_string="$db_pwd" --target_file="$settings"

View file

@ -101,7 +101,7 @@ ynh_system_user_create --username="$app" --home_dir="$final_path" --use_shell
#================================================= #=================================================
# PIP INSTALLATION # 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" python3 -m venv "${final_path}/venv"
cp ../conf/requirements.txt "$final_path/requirements.txt" cp ../conf/requirements.txt "$final_path/requirements.txt"
@ -119,10 +119,7 @@ chown -R "$app" "$final_path"
#================================================= #=================================================
# copy config files # copy config files
# ================================================ # ================================================
ynh_script_progression --message="Create pyinventory configuration file..." ynh_script_progression --message="Create project configuration files..."
cp ../conf/create_superuser.py "$final_path/create_superuser.py"
chmod +x "$final_path/create_superuser.py"
gunicorn_conf="$final_path/gunicorn.conf.py" gunicorn_conf="$final_path/gunicorn.conf.py"
cp "../conf/gunicorn.conf.py" "$gunicorn_conf" 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" cp ../conf/manage.py "$final_path/manage.py"
chmod +x "$final_path/manage.py" chmod +x "$final_path/manage.py"
cp ../conf/wsgi.py "$final_path/wsgi.py" settings="$final_path/settings.py"
cp "../conf/settings.py" "$settings"
settings="$final_path/ynh_pyinventory_settings.py"
cp "../conf/ynh_pyinventory_settings.py" "$settings"
ynh_replace_string --match_string="__APP__" --replace_string="$app" --target_file="$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" 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" 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" touch "$final_path/local_settings.py"
cp "../conf/ynh_urls.py" "$final_path/ynh_urls.py"
#================================================= #=================================================
# MIGRATE / COLLECTSTATIC / CREATEADMIN # MIGRATE / COLLECTSTATIC / CREATEADMIN
@ -177,7 +173,9 @@ ynh_script_progression --message="migrate/collectstatic/createadmin..." --weight
./manage.py migrate --no-input ./manage.py migrate --no-input
./manage.py collectstatic --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 # Check the configuration
# This may fail in some cases with errors, etc., but the app works and the user can fix issues later. # This may fail in some cases with errors, etc., but the app works and the user can fix issues later.

View file

@ -83,10 +83,9 @@ ynh_script_progression --message="Configuring a systemd service..."
ynh_add_systemd_config --service="$app" --template="pyinventory.service" ynh_add_systemd_config --service="$app" --template="pyinventory.service"
#================================================= #=================================================
# UPGRADE PYINVENTORY # UPGRADE VENV
#================================================= #=================================================
ynh_script_progression --message="Upgrade project via pip..." --weight=80
ynh_script_progression --message="Install pyinventory using PIP..." --weight=15
python3 -m venv "${final_path}/venv" python3 -m venv "${final_path}/venv"
cp ../conf/requirements.txt "$final_path/requirements.txt" cp ../conf/requirements.txt "$final_path/requirements.txt"
@ -104,11 +103,7 @@ chown -R "$app" "$final_path"
#================================================= #=================================================
# copy config files # copy config files
# ================================================ # ================================================
ynh_script_progression --message="Create pyinventory configuration file..." ynh_script_progression --message="Create project configuration files..."
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"
gunicorn_conf="$final_path/gunicorn.conf.py" gunicorn_conf="$final_path/gunicorn.conf.py"
ynh_backup_if_checksum_is_different --file="$gunicorn_conf" 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" cp ../conf/manage.py "$final_path/manage.py"
chmod +x "$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 # save old settings file
settings="$final_path/ynh_pyinventory_settings.py" settings="$final_path/settings.py"
ynh_backup_if_checksum_is_different --file="$settings" 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="__APP__" --replace_string="$app" --target_file="$settings"
ynh_replace_string --match_string="__DB_PWD__" --replace_string="$db_pwd" --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 # Recalculate and store the config file checksum into the app settings
ynh_store_file_checksum --file="$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" ynh_backup_if_checksum_is_different --file="$final_path/urls.py"
cp "../conf/ynh_urls.py" "$final_path/ynh_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 # MIGRATE PYINVENTORY
@ -167,7 +165,9 @@ ynh_script_progression --message="migrate/collectstatic/createadmin..." --weight
./manage.py migrate --no-input ./manage.py migrate --no-input
./manage.py collectstatic --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 # Check the configuration
# This may fail in some cases with errors, etc., but the app works and the user can fix issues later. # This may fail in some cases with errors, etc., but the app works and the user can fix issues later.