Modify: settings - add DATA_DIR logs_collector storage_info use DATA_DIR as storage root

This commit is contained in:
Stepan Zhukovsky 2023-09-11 13:53:03 +09:00
parent 5893920d69
commit 57a758f93e
8 changed files with 112 additions and 24 deletions

View File

@ -22,6 +22,7 @@ ENV PYTHONUNBUFFERED 1
# default build args
ARG VERSION=0.1.0 \
APP_DIR=/app \
DATA_DIR=/app/data \
SRC_DIR=./logs_collector \
SCRIPTS_DIR=./scripts \
WEB_PORT=8000 \
@ -34,17 +35,20 @@ ARG VERSION=0.1.0 \
COPY --from=base /usr/local/lib/python3.10/site-packages/ /usr/local/lib/python3.10/site-packages/
COPY --from=base /usr/local/bin/ /usr/local/bin/
# add curl and createa user to avoid running container as root
# add curl and createa user to avoid running container as root &&
# create storage dir
RUN apk add --no-cache --upgrade curl && \
addgroup --system ${USER_GROUP} --gid ${APP_GID} && \
adduser --system --uid ${APP_UID} --ingroup ${USER_GROUP} ${USER_NAME}
adduser --system --uid ${APP_UID} --ingroup ${USER_GROUP} ${USER_NAME} && \
mkdir -p ${APP_DIR}/data && \
chown -R ${USER_NAME}:${USER_GROUP} ${DATA_DIR}
# switch to user
USER ${USER_NAME}
# copy src and entrypoint.sh to app dir
COPY --chown=${USER_NAME}:${USER_GROUP} ${SRC_DIR} ${APP_DIR}
COPY --chown=${USER_NAME}:${USER_GROUP} ${SCRIPTS_DIR}/entrypoint.sh ${APP_DIR}/
COPY --chown=${USER_NAME}:${USER_GROUP} ${SCRIPTS_DIR}/entrypoint.sh ${APP_DIR}
# set workdir
WORKDIR ${APP_DIR}

View File

@ -13,6 +13,7 @@ services:
- SRC_DIR=${SRC_DIR}
- SCRIPTS_DIR=${SCRIPTS_DIR}
- APP_DIR=${APP_DIR}
- DATA_DIR=${DATA_DIR}
- WEB_PORT=${WEB_PORT}
- USER_NAME=${USER_NAME}
- USER_GROUP=${USER_GROUP}

View File

@ -66,3 +66,4 @@ class StorageInfoSerializer(serializers.Serializer):
used = serializers.IntegerField(read_only=True)
free = serializers.IntegerField(read_only=True)
used_percent = serializers.IntegerField(read_only=True)
status = serializers.CharField(read_only=True)

View File

@ -171,4 +171,4 @@ class StorageInfo(views.APIView):
summary='Show storage space in bytes'
)
def get(self, request):
return Response(get_mount_fs_info(settings.MEDIA_ROOT))
return Response(get_mount_fs_info(settings.DATA_DIR))

View File

@ -13,4 +13,4 @@ def metadata(request):
def storage_info(request):
return {'storage': get_mount_fs_info(settings.MEDIA_ROOT)}
return {'storage': get_mount_fs_info(settings.DATA_DIR)}

View File

@ -1,7 +1,8 @@
import shutil
import pathlib
def logs_dir_path(instance, filename):
def logs_dir_path(instance, filename: str) -> str:
"""
file will be uploaded to
MEDIA_ROOT/view/<filename>
@ -30,9 +31,31 @@ def sizify(value: int) -> str:
return f'{round(value, 1)} {ext}'
def get_mount_fs_info(path):
mount_info = shutil.disk_usage(path)._asdict()
mount_info['used_percent'] = round(
mount_info['used'] / mount_info['total'] * 100
)
def get_mount_fs_info(path: type[pathlib.PosixPath]) -> dict:
"""
Get directory information for storing uploaded files.
Includes information total/used/free space on mount device
Args:
path (pathlib.PosixPath): path to storage dir
Returns:
dict: storage mount info
"""
mount_info: dict = {}
try:
mount_info = shutil.disk_usage(path)._asdict()
mount_info['used_percent'] = round(
mount_info['used'] / mount_info['total'] * 100,
)
mount_info['status'] = 'mount'
except Exception as error: # expected FileNotFoundError
mount_info = {
'total': 0,
'used': 0,
'free': 0,
'used_percent': 0,
'status': 'error',
'traceback': f'{error}'
}
return mount_info

View File

@ -4,9 +4,19 @@ from datetime import timedelta
from . import __version__, __status__
# █▀█ █▀█ █▀█ ▀█▀ ▀
# █▀▄ █▄█ █▄█ ░█░ ▄
# -- -- -- -- -- --
# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent
# █▀▀ █▄░█ █░█ ▀
# ██▄ █░▀█ ▀▄▀ ▄
# -- -- -- -- --
# Set default environ variables:
env = environ.Env(
# set casting default value
@ -14,7 +24,7 @@ env = environ.Env(
ENVIRONMENT=(str, __status__),
DEBUG=(bool, False),
SECRET_KEY=(str, 'j9QGbvM9Z4otb47'),
SQLITE_URL=(str, f'sqlite:///{BASE_DIR / "data/db.sqlite3"}'),
DATA_DIR=(str, BASE_DIR / 'data'),
CSRF_TRUSTED_ORIGINS=(list, []),
ALLOWED_HOSTS=(list, ['*']),
TZ=(str, 'UTC'),
@ -23,7 +33,13 @@ env = environ.Env(
# Read .env file if exist:
environ.Env.read_env(BASE_DIR / '.env')
# █▀▀ █▀█ █▀█ █▀▀ ▀
# █▄▄ █▄█ █▀▄ ██▄ ▄
# -- -- -- -- -- -
VERSION = env('VERSION')
ENVIRONMENT = env('ENVIRONMENT')
# SECURITY WARNING: keep the secret key used in production secret!
@ -99,12 +115,6 @@ TEMPLATES = [
WSGI_APPLICATION = 'logs_collector.wsgi.application'
# Database
# https://docs.djangoproject.com/en/4.2/ref/settings/#databases
DATABASES = {
'default': env.db_url('SQLITE_URL')
}
# Password validation
# https://docs.djangoproject.com/en/4.2/ref/settings/#auth-password-validators
@ -134,6 +144,11 @@ USE_I18N = True
USE_TZ = True
# █▀ ▀█▀ ▄▀█ ▀█▀ █ █▀▀ ▀
# ▄█ ░█░ █▀█ ░█░ █ █▄▄ ▄
# -- -- -- -- -- -- -- -
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/4.2/howto/static-files/
# Whitenoise:
@ -141,12 +156,19 @@ USE_TZ = True
STATIC_URL = 'static/'
STATIC_ROOT = BASE_DIR / 'static'
# Default primary key field type
# https://docs.djangoproject.com/en/4.2/ref/settings/#default-auto-field
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
# █▀▄ ▄▀█ ▀█▀ ▄▀█ ▀
# █▄▀ █▀█ ░█░ █▀█ ▄
# -- -- -- -- -- --
MEDIA_ROOT = BASE_DIR / 'data/archives'
# Build paths inside the project for db and storage.
DATA_DIR = Path(env('DATA_DIR'))
# Create DATA_DIR ignore if exist:
Path(DATA_DIR).mkdir(parents=True, exist_ok=True)
# Custom file storage path
MEDIA_ROOT = DATA_DIR / 'archives'
STORAGES = {
"default": {
@ -161,6 +183,29 @@ STORAGES = {
},
}
# █▀▄ ▄▀█ ▀█▀ ▄▀█ █▄▄ ▄▀█ █▀ █▀▀ ▀
# █▄▀ █▀█ ░█░ █▀█ █▄█ █▀█ ▄█ ██▄ ▄
# -- -- -- -- -- -- -- -- -- -- --
# Database
# https://docs.djangoproject.com/en/4.2/ref/settings/#databases
DATABASES = {
'default': env.db_url(
'DB_URL',
default=f'sqlite:///{DATA_DIR / "db.sqlite3"}'
)
}
# Default primary key field type
# https://docs.djangoproject.com/en/4.2/ref/settings/#default-auto-field
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
# █▀▀ ▀▄▀ ▀█▀ █▀▀ █▄░█ ▀█▀ █ █▀█ █▄░█ █▀ ▀
# ██▄ █░█ ░█░ ██▄ █░▀█ ░█░ █ █▄█ █░▀█ ▄█ ▄
# -- -- -- -- -- -- -- -- -- -- -- -- -- -
# django-crispy-forms and crispy-bootstrap5
# https://django-crispy-forms.readthedocs.io/en/latest/
CRISPY_ALLOWED_TEMPLATE_PACKS = "bootstrap5"
@ -196,7 +241,6 @@ if DEBUG:
).append('rest_framework.renderers.BrowsableAPIRenderer')
# https://drf-spectacular.readthedocs.io/en/latest/readme.html
# TODO: set environ vars config!
SPECTACULAR_SETTINGS = {
'TITLE': 'Logs collector API',
'DESCRIPTION': 'Collector of archives with log files for further analysis',
@ -249,6 +293,11 @@ SIMPLE_JWT = {
"SLIDING_TOKEN_REFRESH_SERIALIZER": "rest_framework_simplejwt.serializers.TokenRefreshSlidingSerializer", # noqa:E501
}
# ▄▀█ █░█ ▀█▀ █░█ ▀
# █▀█ █▄█ ░█░ █▀█ ▄
# -- -- -- -- -- --
LOGIN_URL = 'two_factor:login'
LOGIN_REDIRECT_URL = 'collector:index'
LOGOUT_REDIRECT_URL = 'two_factor:login'

View File

@ -5,8 +5,18 @@
class="nav-link me-1 bi bi-sd-card"
aria-current="page"
data-bs-toggle="tooltip"
data-bs-html="true"
data-bs-placement="bottom"
data-bs-title="Storage used: {{ storage.used_percent }}%"
data-bs-title="
<span><u>STORAGE</u><span>
<br>
Used: {{ storage.used_percent }}%
<br>
Status:
<span class={% if storage.status == 'error' %}text-danger{% else %}text-success{% endif %}>
{{ storage.status }}
<span>
"
>
</i>
<div