From 7bec3ff5ddf81230d92cc5353b12c877b2fd18d7 Mon Sep 17 00:00:00 2001 From: Oliver Walters <oliver.henry.walters@gmail.com> Date: Sat, 6 Mar 2021 20:58:57 +1100 Subject: [PATCH 001/111] django-q --- InvenTree/InvenTree/settings.py | 13 +++++++++++++ requirements.txt | 1 + 2 files changed, 14 insertions(+) diff --git a/InvenTree/InvenTree/settings.py b/InvenTree/InvenTree/settings.py index 1a298240bc..0826c7d042 100644 --- a/InvenTree/InvenTree/settings.py +++ b/InvenTree/InvenTree/settings.py @@ -211,6 +211,7 @@ INSTALLED_APPS = [ 'djmoney', # django-money integration 'djmoney.contrib.exchange', # django-money exchange rates 'error_report', # Error reporting in the admin interface + 'django_q', ] MIDDLEWARE = CONFIG.get('middleware', [ @@ -285,6 +286,18 @@ REST_FRAMEWORK = { WSGI_APPLICATION = 'InvenTree.wsgi.application' +# django-q configuration +Q_CLUSTER = { + 'name': 'InvenTree', + 'workers': 4, + 'timeout': 90, + 'retry': 120, + 'queue_limit': 50, + 'bulk': 10, + 'orm': 'default', + 'sync': True, +} + # Markdownx configuration # Ref: https://neutronx.github.io/django-markdownx/customization/ MARKDOWNX_MEDIA_PATH = datetime.now().strftime('markdownx/%Y/%m/%d') diff --git a/requirements.txt b/requirements.txt index 7bbc14bd54..2c8bd9f3e7 100644 --- a/requirements.txt +++ b/requirements.txt @@ -30,5 +30,6 @@ django-error-report==0.2.0 # Error report viewer for the admin interface django-test-migrations==1.1.0 # Unit testing for database migrations python-barcode[images]==0.13.1 # Barcode generator qrcode[pil]==6.1 # QR code generator +django-q==1.3.4 # Background task scheduling inventree # Install the latest version of the InvenTree API python library From 45b3c68930842b662c9e40958016e0c09b18567e Mon Sep 17 00:00:00 2001 From: Oliver Walters <oliver.henry.walters@gmail.com> Date: Sat, 6 Mar 2021 21:41:19 +1100 Subject: [PATCH 002/111] New status info --- InvenTree/InvenTree/context.py | 14 ++++++++++++-- InvenTree/InvenTree/status.py | 31 +++++++++++++++++++------------ InvenTree/templates/stats.html | 15 ++++++++++++++- 3 files changed, 45 insertions(+), 15 deletions(-) diff --git a/InvenTree/InvenTree/context.py b/InvenTree/InvenTree/context.py index 9fee5deaab..1d07511fa9 100644 --- a/InvenTree/InvenTree/context.py +++ b/InvenTree/InvenTree/context.py @@ -30,10 +30,20 @@ def health_status(request): request._inventree_health_status = True - return { - "system_healthy": InvenTree.status.check_system_health(), + status = { + 'django_q_running': InvenTree.status.is_q_cluster_running(), } + all_healthy = True + + for k in status.keys(): + if status[k] is not True: + all_healthy = False + + status['system_healthy'] = all_healthy + + return status + def status_codes(request): """ diff --git a/InvenTree/InvenTree/status.py b/InvenTree/InvenTree/status.py index ec2422a254..e02e476a51 100644 --- a/InvenTree/InvenTree/status.py +++ b/InvenTree/InvenTree/status.py @@ -1,15 +1,32 @@ """ Provides system status functionality checks. """ +# -*- coding: utf-8 -*- -from django.utils.translation import ugettext as _ +from __future__ import unicode_literals import logging +from django.utils.translation import ugettext as _ + +from django_q.monitor import Stat logger = logging.getLogger(__name__) +def is_q_cluster_running(**kwargs): + """ + Return True if at least one cluster worker is running + """ + + clusters = Stat.get_all() + + for cluster in clusters: + print("Cluster:", cluster) + + return len(clusters) > 0 + + def check_system_health(**kwargs): """ Check that the InvenTree system is running OK. @@ -19,7 +36,7 @@ def check_system_health(**kwargs): result = True - if not check_celery_worker(**kwargs): + if not is_q_cluster_running(**kwargs): result = False logger.warning(_("Celery worker check failed")) @@ -27,13 +44,3 @@ def check_system_health(**kwargs): logger.warning(_("InvenTree system health checks failed")) return result - - -def check_celery_worker(**kwargs): - """ - Check that a celery worker is running. - """ - - # TODO - Checks that the configured celery worker thing is running - - return True diff --git a/InvenTree/templates/stats.html b/InvenTree/templates/stats.html index 7b8a9bb93a..30d3f3d881 100644 --- a/InvenTree/templates/stats.html +++ b/InvenTree/templates/stats.html @@ -13,8 +13,9 @@ <td>{% trans "Instance Name" %}</td> <td>{% inventree_instance_name %}</td> </tr> + {% if user.is_staff %} <tr> - <td><span class='fas fa-exclamation-triangle'></span></td> + <td><span class='fas fa-server'></span></td> <td>{% trans "Server status" %}</td> <td> {% if system_healthy %} @@ -24,6 +25,18 @@ {% endif %} </td> </tr> + <tr> + <td><span class='fas fa-tasks'></span></td> + <td>{% trans "Background Worker" %}</td> + <td> + {% if django_q_running %} + <span class='label label-green'>{% trans "Operational" %}</span> + {% else %} + <span class='label label-red'>{% trans "Not running" %}</span> + {% endif %} + </td> + </tr> + {% endif %} {% if not system_healthy %} {% for issue in system_issues %} From 660fed9196df26ae8f96d35c490608b50cfcda3d Mon Sep 17 00:00:00 2001 From: Oliver Walters <oliver.henry.walters@gmail.com> Date: Wed, 10 Mar 2021 14:03:09 +1100 Subject: [PATCH 003/111] Remove unused code from settings.py --- InvenTree/InvenTree/settings.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/InvenTree/InvenTree/settings.py b/InvenTree/InvenTree/settings.py index 0826c7d042..0a72dce33e 100644 --- a/InvenTree/InvenTree/settings.py +++ b/InvenTree/InvenTree/settings.py @@ -414,11 +414,6 @@ CACHES = { 'default': { 'BACKEND': 'django.core.cache.backends.locmem.LocMemCache', }, - 'qr-code': { - 'BACKEND': 'django.core.cache.backends.locmem.LocMemCache', - 'LOCATION': 'qr-code-cache', - 'TIMEOUT': 3600 - } } # Password validation From 5b68d82fa316486ec820de30ba2ce0cb2506ef54 Mon Sep 17 00:00:00 2001 From: Oliver Walters <oliver.henry.walters@gmail.com> Date: Wed, 10 Mar 2021 14:03:19 +1100 Subject: [PATCH 004/111] Skeleton for background tasks --- InvenTree/InvenTree/tasks.py | 39 ++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 InvenTree/InvenTree/tasks.py diff --git a/InvenTree/InvenTree/tasks.py b/InvenTree/InvenTree/tasks.py new file mode 100644 index 0000000000..0ebfc5a2fd --- /dev/null +++ b/InvenTree/InvenTree/tasks.py @@ -0,0 +1,39 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +import json +import requests +import logging + +from datetime import timedelta + + +logger = logging.getLogger(__name__) + + +def delete_successful_tasks(): + """ + Delete successful task logs + which are more than a week old. + """ + + pass + +def check_for_updates(): + """ + Check if there is an update for InvenTree + """ + + response = requests.get('https://api.github.com/repos/inventree/inventree/releases/latest') + + if not response.status_code == 200: + logger.warning(f'Unexpected status code from GitHub API: {response.status_code}') + return + + data = json.loads(response.text) + + # TODO + + +def test(x): + print(f"Running at task! {x}") \ No newline at end of file From 1532a0c3a18e2e32fc5491dde5b9fe11cb6d2f53 Mon Sep 17 00:00:00 2001 From: Oliver Walters <oliver.henry.walters@gmail.com> Date: Thu, 11 Mar 2021 17:18:57 +1100 Subject: [PATCH 005/111] Add InvenTree/apps.py --- InvenTree/InvenTree/apps.py | 12 ++++++++++++ InvenTree/InvenTree/settings.py | 1 + 2 files changed, 13 insertions(+) create mode 100644 InvenTree/InvenTree/apps.py diff --git a/InvenTree/InvenTree/apps.py b/InvenTree/InvenTree/apps.py new file mode 100644 index 0000000000..a13c5b8d31 --- /dev/null +++ b/InvenTree/InvenTree/apps.py @@ -0,0 +1,12 @@ +# -*- coding: utf-8 -*- + +from django.apps import AppConfig + + +class InvenTreeConfig(AppConfig): + name = 'InvenTree' + + def ready(self): + + print("Starting background tasks") + pass diff --git a/InvenTree/InvenTree/settings.py b/InvenTree/InvenTree/settings.py index 0a72dce33e..323eade258 100644 --- a/InvenTree/InvenTree/settings.py +++ b/InvenTree/InvenTree/settings.py @@ -188,6 +188,7 @@ INSTALLED_APPS = [ 'build.apps.BuildConfig', 'common.apps.CommonConfig', 'company.apps.CompanyConfig', + 'InvenTree.apps.InvenTreeConfig', 'label.apps.LabelConfig', 'order.apps.OrderConfig', 'part.apps.PartConfig', From 3cf5aec289fdac940a74ca6278f2503837fe2b6f Mon Sep 17 00:00:00 2001 From: Oliver Walters <oliver.henry.walters@gmail.com> Date: Thu, 11 Mar 2021 19:21:28 +1100 Subject: [PATCH 006/111] Refactor --- InvenTree/InvenTree/apps.py | 28 ++++++++++++++++++++++++++-- InvenTree/InvenTree/settings.py | 2 +- InvenTree/InvenTree/tasks.py | 29 +++++++++++++++++++++++++++++ 3 files changed, 56 insertions(+), 3 deletions(-) diff --git a/InvenTree/InvenTree/apps.py b/InvenTree/InvenTree/apps.py index a13c5b8d31..0b5283706c 100644 --- a/InvenTree/InvenTree/apps.py +++ b/InvenTree/InvenTree/apps.py @@ -1,6 +1,12 @@ # -*- coding: utf-8 -*- from django.apps import AppConfig +import logging + +import InvenTree.tasks + + +logger = logging.getLogger(__name__) class InvenTreeConfig(AppConfig): @@ -8,5 +14,23 @@ class InvenTreeConfig(AppConfig): def ready(self): - print("Starting background tasks") - pass + self.start_background_tasks() + + def start_background_tasks(self): + + try: + from django_q.models import Schedule + except (AppRegistryNotReady): + return + + logger.info("Starting background tasks...") + + InvenTree.tasks.schedule_task( + 'InvenTree.tasks.delete_successful_tasks', + schedule_type=Schedule.WEEKLY, + ) + + InvenTree.tasks.schedule_task( + 'InvenTree.tasks.check_for_updates', + schedule_type=Schedule.DAILY + ) diff --git a/InvenTree/InvenTree/settings.py b/InvenTree/InvenTree/settings.py index 323eade258..2c96fa8706 100644 --- a/InvenTree/InvenTree/settings.py +++ b/InvenTree/InvenTree/settings.py @@ -188,13 +188,13 @@ INSTALLED_APPS = [ 'build.apps.BuildConfig', 'common.apps.CommonConfig', 'company.apps.CompanyConfig', - 'InvenTree.apps.InvenTreeConfig', 'label.apps.LabelConfig', 'order.apps.OrderConfig', 'part.apps.PartConfig', 'report.apps.ReportConfig', 'stock.apps.StockConfig', 'users.apps.UsersConfig', + 'InvenTree.apps.InvenTreeConfig', # InvenTree app runs last # Third part add-ons 'django_filters', # Extended filter functionality diff --git a/InvenTree/InvenTree/tasks.py b/InvenTree/InvenTree/tasks.py index 0ebfc5a2fd..e34c7e8c21 100644 --- a/InvenTree/InvenTree/tasks.py +++ b/InvenTree/InvenTree/tasks.py @@ -7,10 +7,35 @@ import logging from datetime import timedelta +from django.core.exceptions import AppRegistryNotReady + logger = logging.getLogger(__name__) +def schedule_task(taskname, **kwargs): + """ + Create a scheduled task. + If the task has already been scheduled, ignore! + """ + + try: + from django_q.models import Schedule + except (AppRegistryNotReady): + logger.warning("Could not start background tasks - App registry not ready") + return + + if Schedule.objects.filter(func=taskname).exists(): + logger.info(f"Scheduled task '{taskname}' already exists. (Skipping)") + else: + logger.info(f"Creating scheduled task '{taskname}'") + + Schedule.objects.create( + func=taskname, + **kwargs + ) + + def delete_successful_tasks(): """ Delete successful task logs @@ -32,8 +57,12 @@ def check_for_updates(): data = json.loads(response.text) + print("Response:") + print(data) # TODO + return data + def test(x): print(f"Running at task! {x}") \ No newline at end of file From 18defcff160acca611518b95f9dd444bc0c98a26 Mon Sep 17 00:00:00 2001 From: Oliver Walters <oliver.henry.walters@gmail.com> Date: Thu, 11 Mar 2021 19:56:22 +1100 Subject: [PATCH 007/111] Read version number from GitHub --- InvenTree/InvenTree/tasks.py | 42 ++++++++++++++++++++++++++++------ InvenTree/InvenTree/tests.py | 13 +++++++++++ InvenTree/InvenTree/version.py | 23 +++++++++++++++++++ InvenTree/common/models.py | 2 +- 4 files changed, 72 insertions(+), 8 deletions(-) diff --git a/InvenTree/InvenTree/tasks.py b/InvenTree/InvenTree/tasks.py index e34c7e8c21..61b6885b03 100644 --- a/InvenTree/InvenTree/tasks.py +++ b/InvenTree/InvenTree/tasks.py @@ -1,6 +1,7 @@ # -*- coding: utf-8 -*- from __future__ import unicode_literals +import re import json import requests import logging @@ -35,7 +36,7 @@ def schedule_task(taskname, **kwargs): **kwargs ) - + def delete_successful_tasks(): """ Delete successful task logs @@ -49,6 +50,12 @@ def check_for_updates(): Check if there is an update for InvenTree """ + try: + import common.models + import InvenTree.version + except AppRegistryNotReady: + return + response = requests.get('https://api.github.com/repos/inventree/inventree/releases/latest') if not response.status_code == 200: @@ -57,12 +64,33 @@ def check_for_updates(): data = json.loads(response.text) - print("Response:") - print(data) - # TODO + tag = data.get('tag_name', None) - return data + if not tag: + logger.warning(f"'tag_name' missing from GitHub response") + return + match = re.match(r"^.*(\d+)\.(\d+)\.(\d+).*$", tag) -def test(x): - print(f"Running at task! {x}") \ No newline at end of file + if not len(match.groups()) == 3: + logger.warning(f"Version '{tag}' did not match expected pattern") + return + + try: + latest_version = [int(x) for x in match.groups()] + except (ValueError): + logger.warning(f"Version '{tag}' not integer format") + return + + if not len(latest_version) == 3: + logger.warning(f"Version '{tag}' is not correct format") + return + + logger.info(f"Latest InvenTree version: '{tag}'") + + # Save the version to the database + common.models.InvenTreeSetting.set_setting( + 'INVENTREE_LATEST_VERSION', + tag, + None + ) diff --git a/InvenTree/InvenTree/tests.py b/InvenTree/InvenTree/tests.py index 96f32e7f57..1cb382e338 100644 --- a/InvenTree/InvenTree/tests.py +++ b/InvenTree/InvenTree/tests.py @@ -7,6 +7,7 @@ from django.core.exceptions import ValidationError from .validators import validate_overage, validate_part_name from . import helpers +from . import version from mptt.exceptions import InvalidMove @@ -269,3 +270,15 @@ class TestSerialNumberExtraction(TestCase): with self.assertRaises(ValidationError): e("10, a, 7-70j", 4) + + +class TestVersionNumber(TestCase): + + def test_tuple(self): + + v = version.inventreeVersionTuple() + self.assertEqual(len(v), 3) + + s = '.'.join(v) + + self.assertTrue(s in version.inventreeVersion()) diff --git a/InvenTree/InvenTree/version.py b/InvenTree/InvenTree/version.py index 4d3d546789..6aa5c2616c 100644 --- a/InvenTree/InvenTree/version.py +++ b/InvenTree/InvenTree/version.py @@ -4,9 +4,11 @@ Provides information on the current InvenTree version import subprocess import django +import re import common.models + INVENTREE_SW_VERSION = "0.1.8 pre" # Increment this number whenever there is a significant change to the API that any clients need to know about @@ -23,6 +25,27 @@ def inventreeVersion(): return INVENTREE_SW_VERSION +def inventreeVersionTuple(): + """ Return the InvenTree version string as (maj, min, sub) tuple """ + + match = re.match(r"^.*(\d+)\.(\d+)\.(\d+).*$", INVENTREE_SW_VERSION) + + return [int(g) for g in match.groups()] + + +def versionTupleToInt(version): + """ + Convert a version tuple (x, y, z) to an integer. + This simple integer can then be used for direct version comparison + """ + + n = version[0] * 1000 * 1000 + n += version[1] * 1000 + n += version[2] + + return n + + def inventreeApiVersion(): return INVENTREE_API_VERSION diff --git a/InvenTree/common/models.py b/InvenTree/common/models.py index 06c06bde05..23e0773605 100644 --- a/InvenTree/common/models.py +++ b/InvenTree/common/models.py @@ -486,7 +486,7 @@ class InvenTreeSetting(models.Model): create: If True, create a new setting if the specified key does not exist. """ - if not user.is_staff: + if user is not None and not user.is_staff: return try: From 4925f24ca9983e7f06e2a7bcdd465382b2be6698 Mon Sep 17 00:00:00 2001 From: Oliver Walters <oliver.henry.walters@gmail.com> Date: Thu, 11 Mar 2021 20:07:59 +1100 Subject: [PATCH 008/111] Add "up to date" info to the "about" window --- InvenTree/InvenTree/context.py | 2 ++ InvenTree/InvenTree/version.py | 32 +++++++++++++++++++++++++++++--- InvenTree/templates/about.html | 19 +++++++++++++++---- InvenTree/templates/navbar.html | 17 +++++++++++++---- 4 files changed, 59 insertions(+), 11 deletions(-) diff --git a/InvenTree/InvenTree/context.py b/InvenTree/InvenTree/context.py index 1d07511fa9..43e8b904b9 100644 --- a/InvenTree/InvenTree/context.py +++ b/InvenTree/InvenTree/context.py @@ -42,6 +42,8 @@ def health_status(request): status['system_healthy'] = all_healthy + status['up_to_date'] = InvenTree.version.isInvenTreeUpToDate() + return status diff --git a/InvenTree/InvenTree/version.py b/InvenTree/InvenTree/version.py index 6aa5c2616c..bd11c50882 100644 --- a/InvenTree/InvenTree/version.py +++ b/InvenTree/InvenTree/version.py @@ -25,10 +25,13 @@ def inventreeVersion(): return INVENTREE_SW_VERSION -def inventreeVersionTuple(): +def inventreeVersionTuple(version=None): """ Return the InvenTree version string as (maj, min, sub) tuple """ - match = re.match(r"^.*(\d+)\.(\d+)\.(\d+).*$", INVENTREE_SW_VERSION) + if version is None: + version = INVENTREE_SW_VERSION + + match = re.match(r"^.*(\d+)\.(\d+)\.(\d+).*$", str(version)) return [int(g) for g in match.groups()] @@ -44,7 +47,30 @@ def versionTupleToInt(version): n += version[2] return n - + + +def isInvenTreeUpToDate(): + """ + Test if the InvenTree instance is "up to date" with the latest version. + + A background task periodically queries GitHub for latest version, + and stores it to the database as INVENTREE_LATEST_VERSION + """ + + latest = common.models.InvenTreeSetting.get_setting('INVENTREE_LATEST_VERSION', None) + + # No record for "latest" version - we must assume we are up to date! + if not latest: + return True + + # Extract "tuple" version + version = inventreeVersionTuple(latest) + version_int = versionTupleToInt(version) + + inventree_int = versionTupleToInt(inventreeVersionTuple()) + + return inventree_int >= version_int + def inventreeApiVersion(): return INVENTREE_API_VERSION diff --git a/InvenTree/templates/about.html b/InvenTree/templates/about.html index cedfb40ca1..30f9bd19d7 100644 --- a/InvenTree/templates/about.html +++ b/InvenTree/templates/about.html @@ -19,19 +19,30 @@ <col width='25'> <tr> <td><span class='fas fa-hashtag'></span></td> - <td>{% trans "InvenTree Version" %}</td><td><a href="https://github.com/inventree/InvenTree/releases">{% inventree_version %}</a></td> + <td>{% trans "InvenTree Version" %}</td> + <td> + <a href="https://github.com/inventree/InvenTree/releases">{% inventree_version %}</a> + {% if up_to_date %} + <span class='label label-green float-right'>{% trans "Up to Date" %}</span> + {% else %} + <span class='label label-red float-right'>{% trans "Update Available" %}</span> + {% endif %} + </td> </tr> <tr> <td><span class='fas fa-hashtag'></span></td> - <td>{% trans "Django Version" %}</td><td><a href="https://www.djangoproject.com/">{% django_version %}</a></td> + <td>{% trans "Django Version" %}</td> + <td><a href="https://www.djangoproject.com/">{% django_version %}</a></td> </tr> <tr> <td><span class='fas fa-code-branch'></span></td> - <td>{% trans "Commit Hash" %}</td><td><a href="https://github.com/inventree/InvenTree/commit/{% inventree_commit_hash %}">{% inventree_commit_hash %}</a></td> + <td>{% trans "Commit Hash" %}</td> + <td><a href="https://github.com/inventree/InvenTree/commit/{% inventree_commit_hash %}">{% inventree_commit_hash %}</a></td> </tr> <tr> <td><span class='fas fa-calendar-alt'></span></td> - <td>{% trans "Commit Date" %}</td><td>{% inventree_commit_date %}</td> + <td>{% trans "Commit Date" %}</td> + <td>{% inventree_commit_date %}</td> </tr> <tr> <td><span class='fas fa-book'></span></td> diff --git a/InvenTree/templates/navbar.html b/InvenTree/templates/navbar.html index 6e3fe024ca..c52e87d753 100644 --- a/InvenTree/templates/navbar.html +++ b/InvenTree/templates/navbar.html @@ -59,8 +59,8 @@ {% endif %} <li class='dropdown'> <a class='dropdown-toggle' data-toggle='dropdown' href="#"> - {% if not system_healthy %} - <span title='{% trans "InvenTree server issues detected" %}' class='fas fa-exclamation-triangle icon-red'></span> + {% if not system_healthy or not up_to_date %} + <span class='fas fa-exclamation-triangle icon-red'></span> {% endif %} <span class="fas fa-user"></span> <b>{{ user.get_username }}</b></a> <ul class='dropdown-menu'> @@ -78,11 +78,20 @@ {% if system_healthy %} <span class='fas fa-server'> {% else %} - <span class='fas fa-exclamation-triangle icon-red'> + <span class='fas fa-server icon-red'> {% endif %} </span> {% trans "System Information" %} </a></li> - <li id='launch-about'><a href='#'><span class="fas fa-info-circle"></span> {% trans "About InvenTree" %}</a></li> + <li id='launch-about'> + <a href='#'> + {% if up_to_date %} + <span class="fas fa-info-circle"> + {% else %} + <span class='fas fa-info-circle icon-red'> + {% endif %} + </span> {% trans "About InvenTree" %} + </a> + </li> </ul> </li> </ul> From bfb0cb3b4754109657fb29c353d450f4de94f5dd Mon Sep 17 00:00:00 2001 From: Oliver Walters <oliver.henry.walters@gmail.com> Date: Fri, 12 Mar 2021 15:27:28 +1100 Subject: [PATCH 009/111] Add a "heartbeat" task which runs every 5 minutes - Allows us to track if the worker is running - Due to Stat.get_all() not always working --- InvenTree/InvenTree/apps.py | 10 +++++++++- InvenTree/InvenTree/status.py | 23 ++++++++++++++++++++--- InvenTree/InvenTree/tasks.py | 17 +++++++++++++---- InvenTree/templates/navbar.html | 4 +++- 4 files changed, 45 insertions(+), 9 deletions(-) diff --git a/InvenTree/InvenTree/apps.py b/InvenTree/InvenTree/apps.py index 0b5283706c..902ef2c648 100644 --- a/InvenTree/InvenTree/apps.py +++ b/InvenTree/InvenTree/apps.py @@ -1,8 +1,10 @@ # -*- coding: utf-8 -*- -from django.apps import AppConfig import logging +from django.apps import AppConfig +from django.core.exceptions import AppRegistryNotReady + import InvenTree.tasks @@ -34,3 +36,9 @@ class InvenTreeConfig(AppConfig): 'InvenTree.tasks.check_for_updates', schedule_type=Schedule.DAILY ) + + InvenTree.tasks.schedule_task( + 'InvenTree.tasks.heartbeat', + schedule_type=Schedule.MINUTES, + minutes=5 + ) \ No newline at end of file diff --git a/InvenTree/InvenTree/status.py b/InvenTree/InvenTree/status.py index e02e476a51..1f6b01053c 100644 --- a/InvenTree/InvenTree/status.py +++ b/InvenTree/InvenTree/status.py @@ -6,9 +6,11 @@ Provides system status functionality checks. from __future__ import unicode_literals import logging +from datetime import datetime, timedelta from django.utils.translation import ugettext as _ +from django_q.models import Success from django_q.monitor import Stat logger = logging.getLogger(__name__) @@ -21,10 +23,25 @@ def is_q_cluster_running(**kwargs): clusters = Stat.get_all() - for cluster in clusters: - print("Cluster:", cluster) + if len(clusters) > 0: + return True - return len(clusters) > 0 + """ + Sometimes Stat.get_all() returns []. + In this case we have the 'heartbeat' task running every five minutes. + Check to see if we have a result within the last ten minutes + """ + + now = datetime.now() + past = now - timedelta(minutes=10) + + results = Success.objects.filter( + func='InvenTree.tasks.heartbeat', + started__gte=past + ) + + # If any results are returned, then the background worker is running! + return results.exists() def check_system_health(**kwargs): diff --git a/InvenTree/InvenTree/tasks.py b/InvenTree/InvenTree/tasks.py index 61b6885b03..985b9e24da 100644 --- a/InvenTree/InvenTree/tasks.py +++ b/InvenTree/InvenTree/tasks.py @@ -6,8 +6,6 @@ import json import requests import logging -from datetime import timedelta - from django.core.exceptions import AppRegistryNotReady @@ -37,6 +35,17 @@ def schedule_task(taskname, **kwargs): ) +def heartbeat(): + """ + Simple task which runs at 5 minute intervals, + so we can determine that the background worker + is actually running. + + (There is probably a less "hacky" way of achieving this) + """ + pass + + def delete_successful_tasks(): """ Delete successful task logs @@ -45,6 +54,7 @@ def delete_successful_tasks(): pass + def check_for_updates(): """ Check if there is an update for InvenTree @@ -52,7 +62,6 @@ def check_for_updates(): try: import common.models - import InvenTree.version except AppRegistryNotReady: return @@ -67,7 +76,7 @@ def check_for_updates(): tag = data.get('tag_name', None) if not tag: - logger.warning(f"'tag_name' missing from GitHub response") + logger.warning("'tag_name' missing from GitHub response") return match = re.match(r"^.*(\d+)\.(\d+)\.(\d+).*$", tag) diff --git a/InvenTree/templates/navbar.html b/InvenTree/templates/navbar.html index c52e87d753..acd71f0cd8 100644 --- a/InvenTree/templates/navbar.html +++ b/InvenTree/templates/navbar.html @@ -59,8 +59,10 @@ {% endif %} <li class='dropdown'> <a class='dropdown-toggle' data-toggle='dropdown' href="#"> - {% if not system_healthy or not up_to_date %} + {% if not system_healthy %} <span class='fas fa-exclamation-triangle icon-red'></span> + {% elif not up_to_date %} + <span class='fas fa-info-circle icon-green'></span> {% endif %} <span class="fas fa-user"></span> <b>{{ user.get_username }}</b></a> <ul class='dropdown-menu'> From 5b8eb1c53089cd45d91725a937b386ee93a478ee Mon Sep 17 00:00:00 2001 From: Oliver Walters <oliver.henry.walters@gmail.com> Date: Fri, 12 Mar 2021 15:27:53 +1100 Subject: [PATCH 010/111] Newline --- InvenTree/InvenTree/apps.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/InvenTree/InvenTree/apps.py b/InvenTree/InvenTree/apps.py index 902ef2c648..1022a75301 100644 --- a/InvenTree/InvenTree/apps.py +++ b/InvenTree/InvenTree/apps.py @@ -41,4 +41,4 @@ class InvenTreeConfig(AppConfig): 'InvenTree.tasks.heartbeat', schedule_type=Schedule.MINUTES, minutes=5 - ) \ No newline at end of file + ) From 006dd10a7958de7e565c766458d8dc60fbc615e5 Mon Sep 17 00:00:00 2001 From: Oliver Walters <oliver.henry.walters@gmail.com> Date: Fri, 12 Mar 2021 15:35:33 +1100 Subject: [PATCH 011/111] Delete successful tasks more than a month old --- InvenTree/InvenTree/tasks.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/InvenTree/InvenTree/tasks.py b/InvenTree/InvenTree/tasks.py index 985b9e24da..def9abc35d 100644 --- a/InvenTree/InvenTree/tasks.py +++ b/InvenTree/InvenTree/tasks.py @@ -6,6 +6,7 @@ import json import requests import logging +from datetime import datetime, timedelta from django.core.exceptions import AppRegistryNotReady @@ -49,10 +50,16 @@ def heartbeat(): def delete_successful_tasks(): """ Delete successful task logs - which are more than a week old. + which are more than a month old. """ - pass + threshold = datetime.now() - timedelta(days=30) + + results = Success.objects.filter( + started__lte=threshold + ) + + results.delete() def check_for_updates(): From ef4dbda22304e6a2261abc960d4d5d449df3353d Mon Sep 17 00:00:00 2001 From: Oliver Walters <oliver.henry.walters@gmail.com> Date: Fri, 12 Mar 2021 15:35:55 +1100 Subject: [PATCH 012/111] Catch errors if the DB is not up --- InvenTree/InvenTree/tasks.py | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/InvenTree/InvenTree/tasks.py b/InvenTree/InvenTree/tasks.py index def9abc35d..3dcfb66422 100644 --- a/InvenTree/InvenTree/tasks.py +++ b/InvenTree/InvenTree/tasks.py @@ -7,7 +7,9 @@ import requests import logging from datetime import datetime, timedelta + from django.core.exceptions import AppRegistryNotReady +from django.db.utils import OperationalError, ProgrammingError logger = logging.getLogger(__name__) @@ -25,15 +27,19 @@ def schedule_task(taskname, **kwargs): logger.warning("Could not start background tasks - App registry not ready") return - if Schedule.objects.filter(func=taskname).exists(): - logger.info(f"Scheduled task '{taskname}' already exists. (Skipping)") - else: - logger.info(f"Creating scheduled task '{taskname}'") + try: + if Schedule.objects.filter(func=taskname).exists(): + logger.info(f"Scheduled task '{taskname}' already exists. (Skipping)") + else: + logger.info(f"Creating scheduled task '{taskname}'") - Schedule.objects.create( - func=taskname, - **kwargs - ) + Schedule.objects.create( + func=taskname, + **kwargs + ) + except (OperationalError, ProgrammingError): + # Required if the DB is not ready yet + pass def heartbeat(): From 9d404afec0be035e8166fa1ee70934b27080028b Mon Sep 17 00:00:00 2001 From: Oliver Walters <oliver.henry.walters@gmail.com> Date: Fri, 12 Mar 2021 16:00:25 +1100 Subject: [PATCH 013/111] Add 'ignore' rules for the django-q tables --- InvenTree/users/models.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/InvenTree/users/models.py b/InvenTree/users/models.py index 5c95abfe46..57821bb22f 100644 --- a/InvenTree/users/models.py +++ b/InvenTree/users/models.py @@ -132,6 +132,13 @@ class RuleSet(models.Model): 'error_report_error', 'exchange_rate', 'exchange_exchangebackend', + + # Django-q + 'django_q_ormq', + 'django_q_failure', + 'django_q_task', + 'django_q_schedule', + 'django_q_success', ] RULE_OPTIONS = [ From 18b559fee7cb4fcae802c8f1a76521f635fd2f02 Mon Sep 17 00:00:00 2001 From: Oliver Walters <oliver.henry.walters@gmail.com> Date: Fri, 12 Mar 2021 16:28:54 +1100 Subject: [PATCH 014/111] Fix for unit test --- InvenTree/InvenTree/tests.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/InvenTree/InvenTree/tests.py b/InvenTree/InvenTree/tests.py index 1cb382e338..5fadb7b68d 100644 --- a/InvenTree/InvenTree/tests.py +++ b/InvenTree/InvenTree/tests.py @@ -279,6 +279,6 @@ class TestVersionNumber(TestCase): v = version.inventreeVersionTuple() self.assertEqual(len(v), 3) - s = '.'.join(v) + s = '.'.join([str(i) for i in v]) self.assertTrue(s in version.inventreeVersion()) From 700effcee794be4c46fd6c14b633bc1142ed6a51 Mon Sep 17 00:00:00 2001 From: Oliver Walters <oliver.henry.walters@gmail.com> Date: Fri, 12 Mar 2021 16:57:27 +1100 Subject: [PATCH 015/111] Remove celery reference --- InvenTree/InvenTree/status.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/InvenTree/InvenTree/status.py b/InvenTree/InvenTree/status.py index 1f6b01053c..f7c955c394 100644 --- a/InvenTree/InvenTree/status.py +++ b/InvenTree/InvenTree/status.py @@ -55,7 +55,7 @@ def check_system_health(**kwargs): if not is_q_cluster_running(**kwargs): result = False - logger.warning(_("Celery worker check failed")) + logger.warning(_("Background worker check failed")) if not result: logger.warning(_("InvenTree system health checks failed")) From de85d614512ce596568bf17caa855b3f71f86419 Mon Sep 17 00:00:00 2001 From: Oliver Walters <oliver.henry.walters@gmail.com> Date: Mon, 15 Mar 2021 08:31:19 +1100 Subject: [PATCH 016/111] Directly compare version tuples, rather than converting to primitive --- InvenTree/InvenTree/version.py | 23 ++++------------------- 1 file changed, 4 insertions(+), 19 deletions(-) diff --git a/InvenTree/InvenTree/version.py b/InvenTree/InvenTree/version.py index bd11c50882..05946059fc 100644 --- a/InvenTree/InvenTree/version.py +++ b/InvenTree/InvenTree/version.py @@ -36,19 +36,6 @@ def inventreeVersionTuple(version=None): return [int(g) for g in match.groups()] -def versionTupleToInt(version): - """ - Convert a version tuple (x, y, z) to an integer. - This simple integer can then be used for direct version comparison - """ - - n = version[0] * 1000 * 1000 - n += version[1] * 1000 - n += version[2] - - return n - - def isInvenTreeUpToDate(): """ Test if the InvenTree instance is "up to date" with the latest version. @@ -63,13 +50,11 @@ def isInvenTreeUpToDate(): if not latest: return True - # Extract "tuple" version - version = inventreeVersionTuple(latest) - version_int = versionTupleToInt(version) + # Extract "tuple" version (Python can directly compare version tuples) + latest_version = inventreeVersionTuple(latest) + inventree_version = inventreeVersionTuple() - inventree_int = versionTupleToInt(inventreeVersionTuple()) - - return inventree_int >= version_int + return inventree_version >= latest_version def inventreeApiVersion(): From f6dd710d6eec11fc81384134703e715ca56643d9 Mon Sep 17 00:00:00 2001 From: Oliver Walters <oliver.henry.walters@gmail.com> Date: Mon, 15 Mar 2021 08:35:06 +1100 Subject: [PATCH 017/111] Automatically delete old heartbeat messages --- InvenTree/InvenTree/tasks.py | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/InvenTree/InvenTree/tasks.py b/InvenTree/InvenTree/tasks.py index 3dcfb66422..ca84f93ee7 100644 --- a/InvenTree/InvenTree/tasks.py +++ b/InvenTree/InvenTree/tasks.py @@ -8,6 +8,7 @@ import logging from datetime import datetime, timedelta +from django_q.models import Success from django.core.exceptions import AppRegistryNotReady from django.db.utils import OperationalError, ProgrammingError @@ -48,9 +49,19 @@ def heartbeat(): so we can determine that the background worker is actually running. - (There is probably a less "hacky" way of achieving this) + (There is probably a less "hacky" way of achieving this)? """ - pass + + threshold = datetime.now() - timedelta(minutes=30) + + # Delete heartbeat results more than half an hour old, + # otherwise they just create extra noise + heartbeats = Success.objects.filter( + func='InvenTree.tasks.heartbeat', + started__lte=threshold + ) + + heartbeats.delete() def delete_successful_tasks(): From 6ea846ce45cbe114e2cb6b9e30ba9e35c7529864 Mon Sep 17 00:00:00 2001 From: Oliver Walters <oliver.henry.walters@gmail.com> Date: Mon, 15 Mar 2021 08:36:27 +1100 Subject: [PATCH 018/111] Add a #TODO --- InvenTree/InvenTree/status.py | 1 + 1 file changed, 1 insertion(+) diff --git a/InvenTree/InvenTree/status.py b/InvenTree/InvenTree/status.py index f7c955c394..8c65bdc302 100644 --- a/InvenTree/InvenTree/status.py +++ b/InvenTree/InvenTree/status.py @@ -24,6 +24,7 @@ def is_q_cluster_running(**kwargs): clusters = Stat.get_all() if len(clusters) > 0: + # TODO - Introspect on any cluster information return True """ From 24823adc6df036bf7b79be366e505c33e3a4247f Mon Sep 17 00:00:00 2001 From: Oliver Walters <oliver.henry.walters@gmail.com> Date: Mon, 15 Mar 2021 08:51:50 +1100 Subject: [PATCH 019/111] Adds unit tests for version number comparison --- InvenTree/InvenTree/tasks.py | 7 ++++++- InvenTree/InvenTree/tests.py | 18 ++++++++++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/InvenTree/InvenTree/tasks.py b/InvenTree/InvenTree/tasks.py index ca84f93ee7..96f53e1e4c 100644 --- a/InvenTree/InvenTree/tasks.py +++ b/InvenTree/InvenTree/tasks.py @@ -8,7 +8,6 @@ import logging from datetime import datetime, timedelta -from django_q.models import Success from django.core.exceptions import AppRegistryNotReady from django.db.utils import OperationalError, ProgrammingError @@ -52,6 +51,12 @@ def heartbeat(): (There is probably a less "hacky" way of achieving this)? """ + try: + from django_q.models import Success + logger.warning("Could not perform heartbeat task - App registry not ready") + except AppRegistryNotReady: + return + threshold = datetime.now() - timedelta(minutes=30) # Delete heartbeat results more than half an hour old, diff --git a/InvenTree/InvenTree/tests.py b/InvenTree/InvenTree/tests.py index 5fadb7b68d..0da45e9373 100644 --- a/InvenTree/InvenTree/tests.py +++ b/InvenTree/InvenTree/tests.py @@ -273,6 +273,9 @@ class TestSerialNumberExtraction(TestCase): class TestVersionNumber(TestCase): + """ + Unit tests for version number functions + """ def test_tuple(self): @@ -282,3 +285,18 @@ class TestVersionNumber(TestCase): s = '.'.join([str(i) for i in v]) self.assertTrue(s in version.inventreeVersion()) + + def test_comparison(self): + """ + Test direct comparison of version numbers + """ + + v_a = version.inventreeVersionTuple('1.2.0') + v_b = version.inventreeVersionTuple('1.2.3') + v_c = version.inventreeVersionTuple('1.2.4') + v_d = version.inventreeVersionTuple('2.0.0') + + self.assertTrue(v_b > v_a) + self.assertTrue(v_c > v_b) + self.assertTrue(v_d > v_c) + self.assertTrue(v_d > v_a) From c1aed51de1aeb7f3bbb0c9378d22349dfb47a38e Mon Sep 17 00:00:00 2001 From: Oliver Walters <oliver.henry.walters@gmail.com> Date: Mon, 15 Mar 2021 09:34:32 +1100 Subject: [PATCH 020/111] Fix import error --- InvenTree/InvenTree/settings.py | 9 +++++++-- InvenTree/InvenTree/tasks.py | 6 ++++++ 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/InvenTree/InvenTree/settings.py b/InvenTree/InvenTree/settings.py index 2c96fa8706..73a38b484d 100644 --- a/InvenTree/InvenTree/settings.py +++ b/InvenTree/InvenTree/settings.py @@ -45,6 +45,8 @@ def get_setting(environment_var, backup_val, default_value=None): return default_value +# Determine if we are running in "test" mode e.g. "manage.py test" +TESTING = 'test' in sys.argv # Build paths inside the project like this: os.path.join(BASE_DIR, ...) BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) @@ -336,7 +338,7 @@ DATABASES = {} When running unit tests, enforce usage of sqlite3 database, so that the tests can be run in RAM without any setup requirements """ -if 'test' in sys.argv: +if TESTING: logger.info('InvenTree: Running tests - Using sqlite3 memory database') DATABASES['default'] = { # Ensure sqlite3 backend is being used @@ -479,7 +481,10 @@ USE_I18N = True USE_L10N = True -USE_TZ = True +# Do not use native timezone support in "test" mode +# It generates a *lot* of cruft in the logs +if not TESTING: + USE_TZ = True DATE_INPUT_FORMATS = [ "%Y-%m-%d", diff --git a/InvenTree/InvenTree/tasks.py b/InvenTree/InvenTree/tasks.py index 96f53e1e4c..4ba34ca7ba 100644 --- a/InvenTree/InvenTree/tasks.py +++ b/InvenTree/InvenTree/tasks.py @@ -75,6 +75,12 @@ def delete_successful_tasks(): which are more than a month old. """ + try: + from django_q.models import Success + logger.warning("Could not perform 'delete_successful_tasks' - App registry not ready") + except AppRegistryNotReady: + return + threshold = datetime.now() - timedelta(days=30) results = Success.objects.filter( From c6e154f996255e7f9e401e12696fd2072e1905f2 Mon Sep 17 00:00:00 2001 From: Oliver Walters <oliver.henry.walters@gmail.com> Date: Mon, 15 Mar 2021 10:15:48 +1100 Subject: [PATCH 021/111] PEP style fixes --- InvenTree/InvenTree/settings.py | 1 + InvenTree/InvenTree/tests.py | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/InvenTree/InvenTree/settings.py b/InvenTree/InvenTree/settings.py index 73a38b484d..6d7c937d54 100644 --- a/InvenTree/InvenTree/settings.py +++ b/InvenTree/InvenTree/settings.py @@ -45,6 +45,7 @@ def get_setting(environment_var, backup_val, default_value=None): return default_value + # Determine if we are running in "test" mode e.g. "manage.py test" TESTING = 'test' in sys.argv diff --git a/InvenTree/InvenTree/tests.py b/InvenTree/InvenTree/tests.py index 0da45e9373..8465473901 100644 --- a/InvenTree/InvenTree/tests.py +++ b/InvenTree/InvenTree/tests.py @@ -275,7 +275,7 @@ class TestSerialNumberExtraction(TestCase): class TestVersionNumber(TestCase): """ Unit tests for version number functions - """ + """ def test_tuple(self): From 283663633aa3341f8fad44a4a0cfa864a32d8980 Mon Sep 17 00:00:00 2001 From: Oliver Walters <oliver.henry.walters@gmail.com> Date: Fri, 19 Mar 2021 21:52:36 +1100 Subject: [PATCH 022/111] First pass at a supervisor.conf file --- deploy/inventree.conf | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 deploy/inventree.conf diff --git a/deploy/inventree.conf b/deploy/inventree.conf new file mode 100644 index 0000000000..5893b19e67 --- /dev/null +++ b/deploy/inventree.conf @@ -0,0 +1,31 @@ +; # Supervisor Config File +; Example configuration file for running InvenTree using supervisor +; There are two separate processes which must be managed: +; +; ## Web Server +; The InvenTree server must be launched and managed as a process +; The recommended way to handle the web server is to use gunicorn +; +; ## Background Tasks +; A background task manager processes long-running and periodic tasks +; InvenTree uses django-q for this purpose + +[supervisord] +environment=INVENTREE_SRC_DIR="/mnt/c/inventree/InvenTree", INVENTREE_BIN_DIR="/mnt/c/inventree/InvenTree" + +[program:inventree-server] +directory=/mnt/c/inventree/InvenTree +command=/mnt/c/inventree/inventree-env/bin/gunicorn -c gunicorn.conf.py InvenTree.wsgi +autostart=true +autorestart=true +startretries=3 +stderr_logfile=/var/log/inventree/server.err.log +stdout_logfile=/var/log/inventree/server.out.log + +[program:inventree-cluster] +directory=/mnt/c/inventree/InvenTree +command=/mnt/c/inventree/inventree-env/bin/python manage.py qcluster +autostart=true +autorestart=true +stderr_logfile=/var/log/inventree/cluster.err.log +stdout_logfile=/var/log/inventree/cluster.out.log From b7718d9c6c59361ae7d269c2d5e3061cfc01286f Mon Sep 17 00:00:00 2001 From: Oliver Walters <oliver.henry.walters@gmail.com> Date: Fri, 19 Mar 2021 22:08:11 +1100 Subject: [PATCH 023/111] Specify user and logfile --- deploy/inventree.conf | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/deploy/inventree.conf b/deploy/inventree.conf index 5893b19e67..8acaf04cac 100644 --- a/deploy/inventree.conf +++ b/deploy/inventree.conf @@ -11,7 +11,8 @@ ; InvenTree uses django-q for this purpose [supervisord] -environment=INVENTREE_SRC_DIR="/mnt/c/inventree/InvenTree", INVENTREE_BIN_DIR="/mnt/c/inventree/InvenTree" +;environment=INVENTREE_SRC_DIR="/mnt/c/inventree/InvenTree", INVENTREE_BIN_DIR="/mnt/c/inventree/InvenTree" +logfile=/var/log/inventree/supervisor.log [program:inventree-server] directory=/mnt/c/inventree/InvenTree @@ -21,6 +22,7 @@ autorestart=true startretries=3 stderr_logfile=/var/log/inventree/server.err.log stdout_logfile=/var/log/inventree/server.out.log +user=inventree [program:inventree-cluster] directory=/mnt/c/inventree/InvenTree @@ -29,3 +31,4 @@ autostart=true autorestart=true stderr_logfile=/var/log/inventree/cluster.err.log stdout_logfile=/var/log/inventree/cluster.out.log +user=inventree \ No newline at end of file From 8fd666e6629653fe3675708d2cc2e5c122dc3c0d Mon Sep 17 00:00:00 2001 From: Oliver Walters <oliver.henry.walters@gmail.com> Date: Mon, 22 Mar 2021 11:20:09 +1100 Subject: [PATCH 024/111] Improvements for "check for updates" task - Let it throw an error if something fails - Errors are caught as "unsuccessful tasks" --- InvenTree/InvenTree/apps.py | 2 +- InvenTree/InvenTree/tasks.py | 21 ++++++++++----------- 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/InvenTree/InvenTree/apps.py b/InvenTree/InvenTree/apps.py index 1022a75301..6a2e363789 100644 --- a/InvenTree/InvenTree/apps.py +++ b/InvenTree/InvenTree/apps.py @@ -29,7 +29,7 @@ class InvenTreeConfig(AppConfig): InvenTree.tasks.schedule_task( 'InvenTree.tasks.delete_successful_tasks', - schedule_type=Schedule.WEEKLY, + schedule_type=Schedule.DAILY, ) InvenTree.tasks.schedule_task( diff --git a/InvenTree/InvenTree/tasks.py b/InvenTree/InvenTree/tasks.py index 4ba34ca7ba..eba145c660 100644 --- a/InvenTree/InvenTree/tasks.py +++ b/InvenTree/InvenTree/tasks.py @@ -21,6 +21,9 @@ def schedule_task(taskname, **kwargs): If the task has already been scheduled, ignore! """ + # If unspecified, repeat indefinitely + repeats = kwargs.pop('repeats', -1) + try: from django_q.models import Schedule except (AppRegistryNotReady): @@ -34,7 +37,9 @@ def schedule_task(taskname, **kwargs): logger.info(f"Creating scheduled task '{taskname}'") Schedule.objects.create( + name=taskname, func=taskname, + repeats=repeats, **kwargs ) except (OperationalError, ProgrammingError): @@ -98,21 +103,20 @@ def check_for_updates(): try: import common.models except AppRegistryNotReady: + # Apps not yet loaded! return response = requests.get('https://api.github.com/repos/inventree/inventree/releases/latest') if not response.status_code == 200: - logger.warning(f'Unexpected status code from GitHub API: {response.status_code}') - return + raise ValueError(f'Unexpected status code from GitHub API: {response.status_code}') data = json.loads(response.text) tag = data.get('tag_name', None) if not tag: - logger.warning("'tag_name' missing from GitHub response") - return + raise ValueError("'tag_name' missing from GitHub response") match = re.match(r"^.*(\d+)\.(\d+)\.(\d+).*$", tag) @@ -120,15 +124,10 @@ def check_for_updates(): logger.warning(f"Version '{tag}' did not match expected pattern") return - try: - latest_version = [int(x) for x in match.groups()] - except (ValueError): - logger.warning(f"Version '{tag}' not integer format") - return + latest_version = [int(x) for x in match.groups()] if not len(latest_version) == 3: - logger.warning(f"Version '{tag}' is not correct format") - return + raise ValueError(f"Version '{tag}' is not correct format") logger.info(f"Latest InvenTree version: '{tag}'") From edbbfff1afe5f137ec14a9dd38ac19f8a43c8293 Mon Sep 17 00:00:00 2001 From: Oliver Walters <oliver.henry.walters@gmail.com> Date: Tue, 23 Mar 2021 19:58:29 +1100 Subject: [PATCH 025/111] Reduce frequency of heartbeat --- InvenTree/InvenTree/apps.py | 2 +- InvenTree/InvenTree/status.py | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/InvenTree/InvenTree/apps.py b/InvenTree/InvenTree/apps.py index 6a2e363789..07f565e012 100644 --- a/InvenTree/InvenTree/apps.py +++ b/InvenTree/InvenTree/apps.py @@ -40,5 +40,5 @@ class InvenTreeConfig(AppConfig): InvenTree.tasks.schedule_task( 'InvenTree.tasks.heartbeat', schedule_type=Schedule.MINUTES, - minutes=5 + minutes=15 ) diff --git a/InvenTree/InvenTree/status.py b/InvenTree/InvenTree/status.py index 8c65bdc302..841a54f92b 100644 --- a/InvenTree/InvenTree/status.py +++ b/InvenTree/InvenTree/status.py @@ -29,12 +29,12 @@ def is_q_cluster_running(**kwargs): """ Sometimes Stat.get_all() returns []. - In this case we have the 'heartbeat' task running every five minutes. - Check to see if we have a result within the last ten minutes + In this case we have the 'heartbeat' task running every 15 minutes. + Check to see if we have a result within the last 20 minutes """ now = datetime.now() - past = now - timedelta(minutes=10) + past = now - timedelta(minutes=20) results = Success.objects.filter( func='InvenTree.tasks.heartbeat', From e3f49b8996a664d5f1b9012327c6954761a8dc97 Mon Sep 17 00:00:00 2001 From: Oliver Walters <oliver.henry.walters@gmail.com> Date: Wed, 24 Mar 2021 08:31:53 +1100 Subject: [PATCH 026/111] Install invoke and gunicorn as part of requirements.txt --- requirements.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/requirements.txt b/requirements.txt index 2c8bd9f3e7..4749a78a91 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,4 @@ +invoke>=1.4.0 # Invoke build tool wheel>=0.34.2 # Wheel Django==3.0.7 # Django package pillow==7.1.0 # Image manipulation @@ -31,5 +32,6 @@ django-test-migrations==1.1.0 # Unit testing for database migrations python-barcode[images]==0.13.1 # Barcode generator qrcode[pil]==6.1 # QR code generator django-q==1.3.4 # Background task scheduling +gunicorn>=20.0.4 # Gunicorn web server inventree # Install the latest version of the InvenTree API python library From ce64feb79dcb084750d53ce936c070a1aa165bb1 Mon Sep 17 00:00:00 2001 From: Oliver Walters <oliver.henry.walters@gmail.com> Date: Wed, 24 Mar 2021 08:32:00 +1100 Subject: [PATCH 027/111] Update supervisor conf file --- deploy/inventree.conf | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/deploy/inventree.conf b/deploy/inventree.conf index 8acaf04cac..a99bbe58e3 100644 --- a/deploy/inventree.conf +++ b/deploy/inventree.conf @@ -11,24 +11,28 @@ ; InvenTree uses django-q for this purpose [supervisord] -;environment=INVENTREE_SRC_DIR="/mnt/c/inventree/InvenTree", INVENTREE_BIN_DIR="/mnt/c/inventree/InvenTree" -logfile=/var/log/inventree/supervisor.log +; Change this path if log files are stored elsewhere +logfile=/home/inventree/log/supervisor.log [program:inventree-server] +user=inventree directory=/mnt/c/inventree/InvenTree command=/mnt/c/inventree/inventree-env/bin/gunicorn -c gunicorn.conf.py InvenTree.wsgi +startsecs=10 autostart=true autorestart=true startretries=3 -stderr_logfile=/var/log/inventree/server.err.log -stdout_logfile=/var/log/inventree/server.out.log -user=inventree +; Change these paths if log files are stored elsewhere +stderr_logfile=/home/inventree/log/server.err.log +stdout_logfile=/home/inventree/log/server.out.log [program:inventree-cluster] +user=inventree directory=/mnt/c/inventree/InvenTree command=/mnt/c/inventree/inventree-env/bin/python manage.py qcluster +startsecs=10 autostart=true autorestart=true -stderr_logfile=/var/log/inventree/cluster.err.log -stdout_logfile=/var/log/inventree/cluster.out.log -user=inventree \ No newline at end of file +; Change these paths if log files are stored elsewhere +stderr_logfile=/home/inventree/log/cluster.err.log +stdout_logfile=/home/inventree/log/cluster.out.log \ No newline at end of file From df0ab2359f8c3911d644cf7aba36f1c0c4400e0c Mon Sep 17 00:00:00 2001 From: Oliver Walters <oliver.henry.walters@gmail.com> Date: Wed, 24 Mar 2021 22:24:47 +1100 Subject: [PATCH 028/111] Remove invoke tasks which perform system commands - tasks.py is now for InvenTree specific tasks only --- InvenTree/InvenTree/settings.py | 1 - InvenTree/config_template.yaml | 31 ++++++++++++++++++------------- deploy/inventree.conf | 8 ++++---- tasks.py | 22 ---------------------- 4 files changed, 22 insertions(+), 40 deletions(-) diff --git a/InvenTree/InvenTree/settings.py b/InvenTree/InvenTree/settings.py index 6d7c937d54..73a38b484d 100644 --- a/InvenTree/InvenTree/settings.py +++ b/InvenTree/InvenTree/settings.py @@ -45,7 +45,6 @@ def get_setting(environment_var, backup_val, default_value=None): return default_value - # Determine if we are running in "test" mode e.g. "manage.py test" TESTING = 'test' in sys.argv diff --git a/InvenTree/config_template.yaml b/InvenTree/config_template.yaml index 18e3197cca..343ab312e4 100644 --- a/InvenTree/config_template.yaml +++ b/InvenTree/config_template.yaml @@ -7,11 +7,9 @@ # with the prefix INVENTREE_DB_ # e.g INVENTREE_DB_NAME / INVENTREE_DB_USER / INVENTREE_DB_PASSWORD database: - # Default configuration - sqlite filesystem database - ENGINE: sqlite3 - NAME: '../inventree_default_db.sqlite3' + # Uncomment (and edit) one of the database configurations below, + # or specify database options using environment variables - # For more complex database installations, further parameters are required # Refer to the django documentation for full list of options # --- Available options: --- @@ -27,14 +25,22 @@ database: # --- Example Configuration - sqlite3 --- # ENGINE: sqlite3 - # NAME: '/path/to/database.sqlite3' + # NAME: '/home/inventree/database.sqlite3' # --- Example Configuration - MySQL --- #ENGINE: django.db.backends.mysql #NAME: inventree - #USER: inventree_username + #USER: inventree #PASSWORD: inventree_password - #HOST: '127.0.0.1' + #HOST: 'localhost' + #PORT: '3306' + + # --- Example Configuration - Postgresql --- + #ENGINE: django.db.backends.postgresql + #NAME: inventree + #USER: inventree + #PASSWORD: inventree_password + #HOST: 'localhost' #PORT: '5432' # Select default system language (default is 'en-us') @@ -86,13 +92,12 @@ cors: # - https://sub.example.com # MEDIA_ROOT is the local filesystem location for storing uploaded files -# By default, it is stored in a directory named 'inventree_media' local to the InvenTree directory -# This should be changed for a production installation -media_root: '../inventree_media' +# By default, it is stored under /home/inventree +media_root: '/home/inventree/media' # STATIC_ROOT is the local filesystem location for storing static files -# By default it is stored in a directory named 'inventree_static' local to the InvenTree directory -static_root: '../inventree_static' +# By default, it is stored under /home/inventree +static_root: '/home/inventree/static' # Optional URL schemes to allow in URL fields # By default, only the following schemes are allowed: ['http', 'https', 'ftp', 'ftps'] @@ -105,7 +110,7 @@ static_root: '../inventree_static' # Backup options # Set the backup_dir parameter to store backup files in a specific location # If unspecified, the local user's temp directory will be used -#backup_dir: '/home/inventree/backup/' +backup_dir: '/home/inventree/backup/' # Permit custom authentication backends #authentication_backends: diff --git a/deploy/inventree.conf b/deploy/inventree.conf index a99bbe58e3..782b6ae2f6 100644 --- a/deploy/inventree.conf +++ b/deploy/inventree.conf @@ -16,8 +16,8 @@ logfile=/home/inventree/log/supervisor.log [program:inventree-server] user=inventree -directory=/mnt/c/inventree/InvenTree -command=/mnt/c/inventree/inventree-env/bin/gunicorn -c gunicorn.conf.py InvenTree.wsgi +directory=/home/inventree/src/InvenTree +command=/home/inventree/env/bin/gunicorn -c gunicorn.conf.py InvenTree.wsgi startsecs=10 autostart=true autorestart=true @@ -28,8 +28,8 @@ stdout_logfile=/home/inventree/log/server.out.log [program:inventree-cluster] user=inventree -directory=/mnt/c/inventree/InvenTree -command=/mnt/c/inventree/inventree-env/bin/python manage.py qcluster +directory=/home/inventree/src/InvenTree +command=/home/inventree/env/bin/python manage.py qcluster startsecs=10 autostart=true autorestart=true diff --git a/tasks.py b/tasks.py index 579887c809..179d73546a 100644 --- a/tasks.py +++ b/tasks.py @@ -231,28 +231,6 @@ def coverage(c): # Generate coverage report c.run('coverage html') -@task -def mysql(c): - """ - Install packages required for using InvenTree with a MySQL database. - """ - - print('Installing packages required for MySQL') - - c.run('sudo apt-get install mysql-server libmysqlclient-dev') - c.run('pip3 install mysqlclient') - -@task -def postgresql(c): - """ - Install packages required for using InvenTree with a PostgreSQL database - """ - - print("Installing packages required for PostgreSQL") - - c.run('sudo apt-get install postgresql postgresql-contrib libpq-dev') - c.run('pip3 install psycopg2') - @task(help={'filename': "Output filename (default = 'data.json')"}) def export_records(c, filename='data.json'): """ From 3a0c68bf5c0cd84b225fcbe32fd2cba12e5217f0 Mon Sep 17 00:00:00 2001 From: Oliver Walters <oliver.henry.walters@gmail.com> Date: Wed, 24 Mar 2021 22:42:04 +1100 Subject: [PATCH 029/111] Add invoke task to start background worker --- tasks.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/tasks.py b/tasks.py index 179d73546a..2d7d395d10 100644 --- a/tasks.py +++ b/tasks.py @@ -111,6 +111,13 @@ def shell(c): manage(c, 'shell', pty=True) +@task +def worker(c): + """ + Run the InvenTree background worker process + """ + + manage(c, 'qcluster', pty=True) @task def superuser(c): From 3ddbb6a6cd9e85f5d81f845ce5ef564db5091a7c Mon Sep 17 00:00:00 2001 From: Oliver Walters <oliver.henry.walters@gmail.com> Date: Tue, 30 Mar 2021 20:53:26 +1100 Subject: [PATCH 030/111] Check for empty values --- InvenTree/InvenTree/settings.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/InvenTree/InvenTree/settings.py b/InvenTree/InvenTree/settings.py index 73a38b484d..2822202f13 100644 --- a/InvenTree/InvenTree/settings.py +++ b/InvenTree/InvenTree/settings.py @@ -362,6 +362,10 @@ else: # Extract database configuration from the config.yaml file db_config = CONFIG.get('database', {}) + # Default action if db_config not specified in yaml file + if not db_config: + db_config = {} + # If a particular database option is not specified in the config file, # look for it in the environmental variables # e.g. INVENTREE_DB_NAME / INVENTREE_DB_USER / etc From 39b2c5f943cc3de5de03e6ca347411c40a4c1cef Mon Sep 17 00:00:00 2001 From: Oliver Walters <oliver.henry.walters@gmail.com> Date: Tue, 30 Mar 2021 21:18:09 +1100 Subject: [PATCH 031/111] Reintroduce default database config --- InvenTree/config_template.yaml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/InvenTree/config_template.yaml b/InvenTree/config_template.yaml index 343ab312e4..c529182bb7 100644 --- a/InvenTree/config_template.yaml +++ b/InvenTree/config_template.yaml @@ -10,6 +10,11 @@ database: # Uncomment (and edit) one of the database configurations below, # or specify database options using environment variables + # Default installation uses a simple sqlite database + # For production, consider changing this! + ENGINE: sqlite3 + NAME: '/home/inventree/database.sqlite3' + # Refer to the django documentation for full list of options # --- Available options: --- From e7ed4c4eab1fdaf8585a57fa1271b8be9a7ef604 Mon Sep 17 00:00:00 2001 From: Oliver Walters <oliver.henry.walters@gmail.com> Date: Tue, 30 Mar 2021 21:24:06 +1100 Subject: [PATCH 032/111] Travis fixes --- .travis.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.travis.yml b/.travis.yml index 52d0ef1c5c..fdf3be5bc7 100644 --- a/.travis.yml +++ b/.travis.yml @@ -14,6 +14,8 @@ addons: - sqlite3 before_install: + - sudo useradd --create-home inventree + - sudo mkdir /home/inventree/media /home/media/static /home/media/backup - sudo apt-get update - sudo apt-get install gettext - sudo apt-get install mysql-server libmysqlclient-dev From 83f8afe113022b6f7ca6d6f3d0ed0caecdd7ba87 Mon Sep 17 00:00:00 2001 From: Oliver Walters <oliver.henry.walters@gmail.com> Date: Tue, 30 Mar 2021 21:33:49 +1100 Subject: [PATCH 033/111] Add github actions --- .github/workflows/ci.yaml | 32 ++++++++++++++++++++++++++++++++ .github/workflows/style.yaml | 11 +++++++++++ 2 files changed, 43 insertions(+) create mode 100644 .github/workflows/ci.yaml create mode 100644 .github/workflows/style.yaml diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml new file mode 100644 index 0000000000..f935bdda7f --- /dev/null +++ b/.github/workflows/ci.yaml @@ -0,0 +1,32 @@ +name: Django CI + +on: + push: + branches: [ $default-branch ] + pull_request: + branches: [ $default-branch ] + +jobs: + + # Run tests on an SQLite database + sqlite: + + runs-on: ubuntu-latest + + strategy: + max-parallel: 4 + matrix: + python-version: [3.7, 3.8, 3.9] + + steps: + - uses: actions/checkout@v2 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v2 + with: + python-version: ${{ matrix.python-version }} + - name: Install dependencies + run: | + pip3 install invoke + invoke install + - name: Run Tests + run: invoke coverage \ No newline at end of file diff --git a/.github/workflows/style.yaml b/.github/workflows/style.yaml new file mode 100644 index 0000000000..90dae47857 --- /dev/null +++ b/.github/workflows/style.yaml @@ -0,0 +1,11 @@ +name: Style Checks + +on: push + +jobs: + pep: + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v2 \ No newline at end of file From 3f257279ee3a53ba7bfa1f27c3daec8842617770 Mon Sep 17 00:00:00 2001 From: Oliver Walters <oliver.henry.walters@gmail.com> Date: Wed, 31 Mar 2021 22:31:50 +1100 Subject: [PATCH 034/111] Specify directories for CI --- .github/workflows/coverage.yaml | 2 ++ .github/workflows/mariadb.yaml | 2 ++ .github/workflows/mysql.yaml | 2 ++ .github/workflows/postgresql.yaml | 2 ++ InvenTree/config_template.yaml | 3 +++ 5 files changed, 11 insertions(+) diff --git a/.github/workflows/coverage.yaml b/.github/workflows/coverage.yaml index 85362ae917..2b883490d2 100644 --- a/.github/workflows/coverage.yaml +++ b/.github/workflows/coverage.yaml @@ -16,6 +16,8 @@ jobs: INVENTREE_DB_NAME: './test_db.sqlite' INVENTREE_DB_ENGINE: django.db.backends.sqlite3 INVENTREE_DEBUG: info + INVENTREE_MEDIA_ROOT: ./media + INVENTREE_STATIC_ROOT: ./static steps: - name: Checkout Code diff --git a/.github/workflows/mariadb.yaml b/.github/workflows/mariadb.yaml index 2ae02c2bd0..f976cfa088 100644 --- a/.github/workflows/mariadb.yaml +++ b/.github/workflows/mariadb.yaml @@ -16,6 +16,8 @@ jobs: INVENTREE_DB_HOST: '127.0.0.1' INVENTREE_DB_PORT: 3306 INVENTREE_DEBUG: info + INVENTREE_MEDIA_ROOT: ./media + INVENTREE_STATIC_ROOT: ./static services: mariadb: diff --git a/.github/workflows/mysql.yaml b/.github/workflows/mysql.yaml index 7d3ee8d6ae..70acad66a1 100644 --- a/.github/workflows/mysql.yaml +++ b/.github/workflows/mysql.yaml @@ -18,6 +18,8 @@ jobs: INVENTREE_DB_HOST: '127.0.0.1' INVENTREE_DB_PORT: 3306 INVENTREE_DEBUG: info + INVENTREE_MEDIA_ROOT: ./media + INVENTREE_STATIC_ROOT: ./static services: mysql: diff --git a/.github/workflows/postgresql.yaml b/.github/workflows/postgresql.yaml index aab05205cc..76981e5a1b 100644 --- a/.github/workflows/postgresql.yaml +++ b/.github/workflows/postgresql.yaml @@ -18,6 +18,8 @@ jobs: INVENTREE_DB_HOST: '127.0.0.1' INVENTREE_DB_PORT: 5432 INVENTREE_DEBUG: info + INVENTREE_MEDIA_ROOT: ./media + INVENTREE_STATIC_ROOT: ./static services: postgres: diff --git a/InvenTree/config_template.yaml b/InvenTree/config_template.yaml index c529182bb7..bab673306f 100644 --- a/InvenTree/config_template.yaml +++ b/InvenTree/config_template.yaml @@ -98,10 +98,12 @@ cors: # MEDIA_ROOT is the local filesystem location for storing uploaded files # By default, it is stored under /home/inventree +# Use environment variable INVENTREE_MEDIA_ROOT media_root: '/home/inventree/media' # STATIC_ROOT is the local filesystem location for storing static files # By default, it is stored under /home/inventree +# Use environment variable INVENTREE_STATIC_ROOT static_root: '/home/inventree/static' # Optional URL schemes to allow in URL fields @@ -115,6 +117,7 @@ static_root: '/home/inventree/static' # Backup options # Set the backup_dir parameter to store backup files in a specific location # If unspecified, the local user's temp directory will be used +# Use environment variable INVENTREE_BACKUP_DIR backup_dir: '/home/inventree/backup/' # Permit custom authentication backends From c0a0ca458873e1bf3aec855b7646b974d675a8d8 Mon Sep 17 00:00:00 2001 From: Oliver Walters <oliver.henry.walters@gmail.com> Date: Wed, 31 Mar 2021 22:35:48 +1100 Subject: [PATCH 035/111] PEP fix --- InvenTree/InvenTree/settings.py | 1 + 1 file changed, 1 insertion(+) diff --git a/InvenTree/InvenTree/settings.py b/InvenTree/InvenTree/settings.py index f49c5d1f75..eee69c0780 100644 --- a/InvenTree/InvenTree/settings.py +++ b/InvenTree/InvenTree/settings.py @@ -45,6 +45,7 @@ def get_setting(environment_var, backup_val, default_value=None): return default_value + # Determine if we are running in "test" mode e.g. "manage.py test" TESTING = 'test' in sys.argv From ab57fd3b764a3e0411c81919a175daecb6627afc Mon Sep 17 00:00:00 2001 From: Oliver Walters <oliver.henry.walters@gmail.com> Date: Wed, 31 Mar 2021 22:45:42 +1100 Subject: [PATCH 036/111] Build docker image --- .github/workflows/docker.yaml | 16 ++++++++++++ docker/Dockerfile | 46 +++++++++++++++++++++++++++++++++++ 2 files changed, 62 insertions(+) create mode 100644 .github/workflows/docker.yaml create mode 100644 docker/Dockerfile diff --git a/.github/workflows/docker.yaml b/.github/workflows/docker.yaml new file mode 100644 index 0000000000..448babcc31 --- /dev/null +++ b/.github/workflows/docker.yaml @@ -0,0 +1,16 @@ +# Test that the docker file builds correctly + +name: Docker + +on: ["push", "pull_request"] + +jobs: + + docker: + runs-on: ubtun-latest + + steps: + - uses: actions/checkout@v2 + - name: Build Docker Image + run: docker build . --file docker/Dockerfile --tag inventree-:$(date +%s) + \ No newline at end of file diff --git a/docker/Dockerfile b/docker/Dockerfile new file mode 100644 index 0000000000..da0ddebf39 --- /dev/null +++ b/docker/Dockerfile @@ -0,0 +1,46 @@ +FROM python:alpine as production + +# Configuration params +ARG INVENTREE_REPO="https://github.com/inventree/InvenTree.git" +ARG INVENTREE_VERSION="master" +ARG INVENTREE_HOME="/home/inventree" + +ENV PYTHONUNBUFFERED 1 + +# InvenTree paths +ENV INVENTREE_SRC_DIR="$INVENTREE_HOME/src" +ENV INVENTREE_STATIC_ROOT="$INVENTREE_HOME/static" +ENV INVENTREE_MEDIA_ROOT="$INVENTREE_HOME/media" +ENV INVENTREE_LOG_DIR="$INVENTREE_HOME/log" +ENV INVENTREE_BACKUP_DIR="$INVENTREE_HOME/backup" +ENV INVENTREE_VENV="$INVENTREE_HOME/env" + +RUN echo "Installing InvenTree '${INVENTREE_VERSION}' from ${INVENTREE_REPO}" + +# Create user account +RUN useradd -ms /bin/bash inventree +USER inventree +WORKDIR /home/inventree + +# Clone source code +RUN git clone --branch $INVENTREE_VERSION --depth 1 ${INVENTREE_REPO} ${INVENTREE_SRC_DIR} + +# Install required system packages +RUN apk add --no-cache postgresql-contrib postgresql-dev libpq-dev +RUN apk add --no-cache libmysqlclient-dev + +# Install required PIP packages +RUN python -m venv $INVENTREE_VENV && pip install --upgrade pip setuptools wheel +RUN python -m venv $INVENTREE_VENV && pip install --no-cache-dir -U invoke +RUN python -m venv $INVENTREE_VENV && pip install --no-cache-dir -U psycopg2 mysqlclient pgcli mariadb + +# Install InvenTree packages +RUN python -m venv $INVENTREE_VENV && pip install --no-cache-dir -U -r $INVENTREE_SRC_DIR/requirements.txt + +# Install supervisor +RUN apt add --no-cache supervisor + +# Copy supervisor file +COPY deploy/inventree.conf /etc/supervisor/conf.d/inventree.conf + +RUN sudo service supervisor start From 1a7b6e26136418cb7bea2289b56c5d902c5cddfd Mon Sep 17 00:00:00 2001 From: Oliver Walters <oliver.henry.walters@gmail.com> Date: Wed, 31 Mar 2021 22:47:41 +1100 Subject: [PATCH 037/111] Fix --- .github/workflows/docker.yaml | 2 +- docker/Dockerfile | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/docker.yaml b/.github/workflows/docker.yaml index 448babcc31..9d08869db3 100644 --- a/.github/workflows/docker.yaml +++ b/.github/workflows/docker.yaml @@ -7,7 +7,7 @@ on: ["push", "pull_request"] jobs: docker: - runs-on: ubtun-latest + runs-on: ubtunu-latest steps: - uses: actions/checkout@v2 diff --git a/docker/Dockerfile b/docker/Dockerfile index da0ddebf39..d2301e66fb 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -40,6 +40,9 @@ RUN python -m venv $INVENTREE_VENV && pip install --no-cache-dir -U -r $INVENTRE # Install supervisor RUN apt add --no-cache supervisor +# Create required directories +RUN mkdir /home/inventree/media /home/inventree/static /home/inventree/log /home/inventree/backup + # Copy supervisor file COPY deploy/inventree.conf /etc/supervisor/conf.d/inventree.conf From 6017cad6b3ed6e4595025ed56803f6198778fadf Mon Sep 17 00:00:00 2001 From: Oliver Walters <oliver.henry.walters@gmail.com> Date: Wed, 31 Mar 2021 22:48:58 +1100 Subject: [PATCH 038/111] So apparently I cannot spell... --- .github/workflows/docker.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docker.yaml b/.github/workflows/docker.yaml index 9d08869db3..270ddc3600 100644 --- a/.github/workflows/docker.yaml +++ b/.github/workflows/docker.yaml @@ -7,7 +7,7 @@ on: ["push", "pull_request"] jobs: docker: - runs-on: ubtunu-latest + runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 From 2746396d11a8fb93cf8bfee5f42de0cb2b396975 Mon Sep 17 00:00:00 2001 From: Oliver Walters <oliver.henry.walters@gmail.com> Date: Wed, 31 Mar 2021 22:50:41 +1100 Subject: [PATCH 039/111] Fix tag name --- .github/workflows/docker.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docker.yaml b/.github/workflows/docker.yaml index 270ddc3600..749616d716 100644 --- a/.github/workflows/docker.yaml +++ b/.github/workflows/docker.yaml @@ -12,5 +12,5 @@ jobs: steps: - uses: actions/checkout@v2 - name: Build Docker Image - run: docker build . --file docker/Dockerfile --tag inventree-:$(date +%s) + run: docker build . --file docker/Dockerfile --tag inventree:$(date +%s) \ No newline at end of file From 58bfc80f793e8bc3ff538cfd0d637a367b38a907 Mon Sep 17 00:00:00 2001 From: Oliver Walters <oliver.henry.walters@gmail.com> Date: Wed, 31 Mar 2021 22:54:17 +1100 Subject: [PATCH 040/111] Alpine uses different commands --- docker/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker/Dockerfile b/docker/Dockerfile index d2301e66fb..de3c79235f 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -18,7 +18,7 @@ ENV INVENTREE_VENV="$INVENTREE_HOME/env" RUN echo "Installing InvenTree '${INVENTREE_VERSION}' from ${INVENTREE_REPO}" # Create user account -RUN useradd -ms /bin/bash inventree +RUN addgroup -S inventreegroup && adduser -S inventree -G inventreegroup USER inventree WORKDIR /home/inventree From 601aff82832b6a20e0d7672da46d17abf22c9617 Mon Sep 17 00:00:00 2001 From: Oliver Walters <oliver.henry.walters@gmail.com> Date: Wed, 31 Mar 2021 22:55:44 +1100 Subject: [PATCH 041/111] Install git --- docker/Dockerfile | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docker/Dockerfile b/docker/Dockerfile index de3c79235f..24f99764d6 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -22,6 +22,9 @@ RUN addgroup -S inventreegroup && adduser -S inventree -G inventreegroup USER inventree WORKDIR /home/inventree +# Install git +RUN apt add --no-cache git + # Clone source code RUN git clone --branch $INVENTREE_VERSION --depth 1 ${INVENTREE_REPO} ${INVENTREE_SRC_DIR} From 42b400e619464bd22fd7328d422880f527eb6687 Mon Sep 17 00:00:00 2001 From: Oliver Walters <oliver.henry.walters@gmail.com> Date: Wed, 31 Mar 2021 22:58:32 +1100 Subject: [PATCH 042/111] typo fix --- docker/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker/Dockerfile b/docker/Dockerfile index 24f99764d6..f4ad7efddd 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -23,7 +23,7 @@ USER inventree WORKDIR /home/inventree # Install git -RUN apt add --no-cache git +RUN apk add --no-cache git # Clone source code RUN git clone --branch $INVENTREE_VERSION --depth 1 ${INVENTREE_REPO} ${INVENTREE_SRC_DIR} From 1f881dd041eaf648981a33eeabea9473abb8c6b3 Mon Sep 17 00:00:00 2001 From: Oliver Walters <oliver.henry.walters@gmail.com> Date: Wed, 31 Mar 2021 23:00:22 +1100 Subject: [PATCH 043/111] Run as root --- docker/Dockerfile | 1 - 1 file changed, 1 deletion(-) diff --git a/docker/Dockerfile b/docker/Dockerfile index f4ad7efddd..a00cf1982a 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -19,7 +19,6 @@ RUN echo "Installing InvenTree '${INVENTREE_VERSION}' from ${INVENTREE_REPO}" # Create user account RUN addgroup -S inventreegroup && adduser -S inventree -G inventreegroup -USER inventree WORKDIR /home/inventree # Install git From 61f8b982ce216d512f32dea0b70f67a07dfe6c4a Mon Sep 17 00:00:00 2001 From: Oliver Walters <oliver.henry.walters@gmail.com> Date: Wed, 31 Mar 2021 23:03:13 +1100 Subject: [PATCH 044/111] lib name fix --- docker/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker/Dockerfile b/docker/Dockerfile index a00cf1982a..c01fca9075 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -28,7 +28,7 @@ RUN apk add --no-cache git RUN git clone --branch $INVENTREE_VERSION --depth 1 ${INVENTREE_REPO} ${INVENTREE_SRC_DIR} # Install required system packages -RUN apk add --no-cache postgresql-contrib postgresql-dev libpq-dev +RUN apk add --no-cache postgresql-contrib postgresql-dev libpq RUN apk add --no-cache libmysqlclient-dev # Install required PIP packages From 251ec7a02f82325b9a30ac7e663874aa0d5d9fc9 Mon Sep 17 00:00:00 2001 From: Oliver Walters <oliver.henry.walters@gmail.com> Date: Wed, 31 Mar 2021 23:06:54 +1100 Subject: [PATCH 045/111] Fix lib names --- docker/Dockerfile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docker/Dockerfile b/docker/Dockerfile index c01fca9075..b049e2f886 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -29,7 +29,8 @@ RUN git clone --branch $INVENTREE_VERSION --depth 1 ${INVENTREE_REPO} ${INVENTRE # Install required system packages RUN apk add --no-cache postgresql-contrib postgresql-dev libpq -RUN apk add --no-cache libmysqlclient-dev +RUN apk add --no-cache libmysqlclient +RUN apk add --no-cache mariadb-connector-c mariadb-dev # Install required PIP packages RUN python -m venv $INVENTREE_VENV && pip install --upgrade pip setuptools wheel From 24d36e0b661ef793f55489e5dbd3b57213fdc303 Mon Sep 17 00:00:00 2001 From: Oliver Walters <oliver.henry.walters@gmail.com> Date: Wed, 31 Mar 2021 23:09:24 +1100 Subject: [PATCH 046/111] Getting there... --- docker/Dockerfile | 1 - 1 file changed, 1 deletion(-) diff --git a/docker/Dockerfile b/docker/Dockerfile index b049e2f886..01f6b16d99 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -29,7 +29,6 @@ RUN git clone --branch $INVENTREE_VERSION --depth 1 ${INVENTREE_REPO} ${INVENTRE # Install required system packages RUN apk add --no-cache postgresql-contrib postgresql-dev libpq -RUN apk add --no-cache libmysqlclient RUN apk add --no-cache mariadb-connector-c mariadb-dev # Install required PIP packages From 286cf9b102b51ed4045676f26d3886e608965f23 Mon Sep 17 00:00:00 2001 From: Oliver Walters <oliver.henry.walters@gmail.com> Date: Wed, 31 Mar 2021 23:12:27 +1100 Subject: [PATCH 047/111] gcc required --- docker/Dockerfile | 1 + 1 file changed, 1 insertion(+) diff --git a/docker/Dockerfile b/docker/Dockerfile index 01f6b16d99..567e3390b0 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -28,6 +28,7 @@ RUN apk add --no-cache git RUN git clone --branch $INVENTREE_VERSION --depth 1 ${INVENTREE_REPO} ${INVENTREE_SRC_DIR} # Install required system packages +RUN apk add --no-cache gcc g++ RUN apk add --no-cache postgresql-contrib postgresql-dev libpq RUN apk add --no-cache mariadb-connector-c mariadb-dev From 8b227ce297ab8d315b91cf0801230d1da7ffa528 Mon Sep 17 00:00:00 2001 From: Oliver Walters <oliver.henry.walters@gmail.com> Date: Wed, 31 Mar 2021 23:20:32 +1100 Subject: [PATCH 048/111] More required packages, I guess... --- README.md | 1 + docker/Dockerfile | 18 +++++++++++------- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 63948c35af..ef0556cceb 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,7 @@ [](https://opensource.org/licenses/MIT) [](https://coveralls.io/github/inventree/InvenTree)  +    diff --git a/docker/Dockerfile b/docker/Dockerfile index 567e3390b0..51f147b670 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -21,17 +21,21 @@ RUN echo "Installing InvenTree '${INVENTREE_VERSION}' from ${INVENTREE_REPO}" RUN addgroup -S inventreegroup && adduser -S inventree -G inventreegroup WORKDIR /home/inventree -# Install git -RUN apk add --no-cache git +# Install required system packages +RUN apk add --no-cache git make bash \ + gcc libgcc g++ libstdc++ \ + libjpeg-turbo libjpeg-turbo-dev jpeg jpeg-dev \ + libffi libffi-dev \ + zlib zlib-dev \ +RUN apk add --no-cache cairo cairo-dev pango pango-dev +RUN apk add --no-cache fontconfig ttf-droid ttf-liberation ttf-dejavu ttf-opensans ttf-ubuntu-font-family font-croscore font-noto +RUN apk add --no-cache python3 +RUN apk add --no-cache postgresql-contrib postgresql-dev libpq +RUN apk add --no-cache mariadb-connector-c mariadb-dev # Clone source code RUN git clone --branch $INVENTREE_VERSION --depth 1 ${INVENTREE_REPO} ${INVENTREE_SRC_DIR} -# Install required system packages -RUN apk add --no-cache gcc g++ -RUN apk add --no-cache postgresql-contrib postgresql-dev libpq -RUN apk add --no-cache mariadb-connector-c mariadb-dev - # Install required PIP packages RUN python -m venv $INVENTREE_VENV && pip install --upgrade pip setuptools wheel RUN python -m venv $INVENTREE_VENV && pip install --no-cache-dir -U invoke From ff6b127f1b52e77c46380cced4ba8919529c19b5 Mon Sep 17 00:00:00 2001 From: Oliver Walters <oliver.henry.walters@gmail.com> Date: Wed, 31 Mar 2021 23:22:17 +1100 Subject: [PATCH 049/111] Typo fixin' --- docker/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker/Dockerfile b/docker/Dockerfile index 51f147b670..6f15ddbd2d 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -26,7 +26,7 @@ RUN apk add --no-cache git make bash \ gcc libgcc g++ libstdc++ \ libjpeg-turbo libjpeg-turbo-dev jpeg jpeg-dev \ libffi libffi-dev \ - zlib zlib-dev \ + zlib zlib-dev RUN apk add --no-cache cairo cairo-dev pango pango-dev RUN apk add --no-cache fontconfig ttf-droid ttf-liberation ttf-dejavu ttf-opensans ttf-ubuntu-font-family font-croscore font-noto RUN apk add --no-cache python3 From 7683cc1aaa4c2d833499792eab3cdba8f6e7d0ce Mon Sep 17 00:00:00 2001 From: Oliver Walters <oliver.henry.walters@gmail.com> Date: Wed, 31 Mar 2021 23:27:01 +1100 Subject: [PATCH 050/111] APK not APT --- docker/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker/Dockerfile b/docker/Dockerfile index 6f15ddbd2d..b5a1826dd2 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -45,7 +45,7 @@ RUN python -m venv $INVENTREE_VENV && pip install --no-cache-dir -U psycopg2 mys RUN python -m venv $INVENTREE_VENV && pip install --no-cache-dir -U -r $INVENTREE_SRC_DIR/requirements.txt # Install supervisor -RUN apt add --no-cache supervisor +RUN apk add --no-cache supervisor # Create required directories RUN mkdir /home/inventree/media /home/inventree/static /home/inventree/log /home/inventree/backup From b9f9b26ca5c2b75ba514037734691a911dfa03a7 Mon Sep 17 00:00:00 2001 From: Oliver Walters <oliver.henry.walters@gmail.com> Date: Wed, 31 Mar 2021 23:32:03 +1100 Subject: [PATCH 051/111] Sudo not required, I guess? --- docker/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker/Dockerfile b/docker/Dockerfile index b5a1826dd2..2b5878f223 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -53,4 +53,4 @@ RUN mkdir /home/inventree/media /home/inventree/static /home/inventree/log /home # Copy supervisor file COPY deploy/inventree.conf /etc/supervisor/conf.d/inventree.conf -RUN sudo service supervisor start +RUN service supervisor start From b9e81c3c0e685890690f6792277e6f65acd111c2 Mon Sep 17 00:00:00 2001 From: Oliver Walters <oliver.henry.walters@gmail.com> Date: Wed, 31 Mar 2021 23:39:16 +1100 Subject: [PATCH 052/111] Start supervisord Ref: https://advancedweb.hu/supervisor-with-docker-lessons-learned/ --- deploy/inventree.conf | 1 + docker/Dockerfile | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/deploy/inventree.conf b/deploy/inventree.conf index 782b6ae2f6..a55c7163f6 100644 --- a/deploy/inventree.conf +++ b/deploy/inventree.conf @@ -13,6 +13,7 @@ [supervisord] ; Change this path if log files are stored elsewhere logfile=/home/inventree/log/supervisor.log +nodaemon=true [program:inventree-server] user=inventree diff --git a/docker/Dockerfile b/docker/Dockerfile index 2b5878f223..c0b088d952 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -53,4 +53,4 @@ RUN mkdir /home/inventree/media /home/inventree/static /home/inventree/log /home # Copy supervisor file COPY deploy/inventree.conf /etc/supervisor/conf.d/inventree.conf -RUN service supervisor start +CMD ["/usr/bin/supervisord"] From 38b9655ad9831eec6410afa21200a1427f316068 Mon Sep 17 00:00:00 2001 From: Oliver Walters <oliver.henry.walters@gmail.com> Date: Thu, 1 Apr 2021 08:43:58 +1100 Subject: [PATCH 053/111] Remove unused workflow --- .github/workflows/ci.yaml | 32 -------------------------------- 1 file changed, 32 deletions(-) delete mode 100644 .github/workflows/ci.yaml diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml deleted file mode 100644 index f935bdda7f..0000000000 --- a/.github/workflows/ci.yaml +++ /dev/null @@ -1,32 +0,0 @@ -name: Django CI - -on: - push: - branches: [ $default-branch ] - pull_request: - branches: [ $default-branch ] - -jobs: - - # Run tests on an SQLite database - sqlite: - - runs-on: ubuntu-latest - - strategy: - max-parallel: 4 - matrix: - python-version: [3.7, 3.8, 3.9] - - steps: - - uses: actions/checkout@v2 - - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v2 - with: - python-version: ${{ matrix.python-version }} - - name: Install dependencies - run: | - pip3 install invoke - invoke install - - name: Run Tests - run: invoke coverage \ No newline at end of file From 76ab38a06b18ef3d05058cfaeb4906c3b17341c4 Mon Sep 17 00:00:00 2001 From: Oliver Walters <oliver.henry.walters@gmail.com> Date: Thu, 1 Apr 2021 11:35:03 +1100 Subject: [PATCH 054/111] Add docker info --- docker/Dockerfile | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/docker/Dockerfile b/docker/Dockerfile index c0b088d952..a0f0595c93 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -15,6 +15,16 @@ ENV INVENTREE_LOG_DIR="$INVENTREE_HOME/log" ENV INVENTREE_BACKUP_DIR="$INVENTREE_HOME/backup" ENV INVENTREE_VENV="$INVENTREE_HOME/env" +LABEL org.label-schema.schema-version="1.0" \ + org.label-schema.build-date=$DATE \ + org.label-schema.vendor="inventree" \ + org.label-schema.name="inventree/inventree" \ + org.label-schema.url="https://hub.docker.com/r/inventree/inventree-docker" \ + org.label-schema.version=$INVENTREE_VERSION \ + org.label-schema.vcs-url=$INVENTREE_REPO \ + org.label-schema.vcs-branch=$BRANCH \ + org.label-schema.vcs-ref=$COMMIT + RUN echo "Installing InvenTree '${INVENTREE_VERSION}' from ${INVENTREE_REPO}" # Create user account From 08a1a6cf43761500a4d79dbb940edacdc9b4e831 Mon Sep 17 00:00:00 2001 From: Oliver Walters <oliver.henry.walters@gmail.com> Date: Thu, 1 Apr 2021 20:14:17 +1100 Subject: [PATCH 055/111] Add configuration options for the Dockerfile --- docker/Dockerfile | 51 +++++++++++++++++++++++++++++++---------------- 1 file changed, 34 insertions(+), 17 deletions(-) diff --git a/docker/Dockerfile b/docker/Dockerfile index a0f0595c93..ffba72f4c6 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -4,26 +4,43 @@ FROM python:alpine as production ARG INVENTREE_REPO="https://github.com/inventree/InvenTree.git" ARG INVENTREE_VERSION="master" ARG INVENTREE_HOME="/home/inventree" +ARG INVENTREE_PORT="80" + +# Database configuration options +ARG INVENTREE_DB_ENGINE="sqlite" +ARG INVENTREE_DB_NAME="inventree_db.sqlite3" +ARG INVENTREE_DB_HOST="127.0.0.1" +ARG INVENTREE_DB_PORT="" +ARG INVENTREE_DB_USER="" +ARG INVENTREE_DB_PASSWORD="" ENV PYTHONUNBUFFERED 1 # InvenTree paths -ENV INVENTREE_SRC_DIR="$INVENTREE_HOME/src" -ENV INVENTREE_STATIC_ROOT="$INVENTREE_HOME/static" -ENV INVENTREE_MEDIA_ROOT="$INVENTREE_HOME/media" -ENV INVENTREE_LOG_DIR="$INVENTREE_HOME/log" -ENV INVENTREE_BACKUP_DIR="$INVENTREE_HOME/backup" -ENV INVENTREE_VENV="$INVENTREE_HOME/env" +ENV INVENTREE_SRC_DIR="${INVENTREE_HOME}/src" +ENV INVENTREE_STATIC_ROOT="${INVENTREE_HOME}/static" +ENV INVENTREE_MEDIA_ROOT="${INVENTREE_HOME}/media" +ENV INVENTREE_LOG_DIR="${INVENTREE_HOME}/log" +ENV INVENTREE_BACKUP_DIR="${INVENTREE_HOME}/backup" +ENV INVENTREE_VENV="${INVENTREE_HOME}/env" + +# Pass DB configuration through as environment variables +ENV INVENTREE_DB_ENGINE="${INVENTREE_DB_ENGINE}" +ENV INVENTREE_DB_NAME="${INVENTREE_DB_NAME}" +ENV INVENTREE_DB_HOST="${INVENTREE_DB_HOST}" +ENV INVENTREE_DB_PORT="${INVENTREE_DB_PORT}" +ENV INVENTREE_DB_USER="${INVENTREE_DB_USER}" +ENV INVENTREE_DB_PASSWORD="${INVENTREE_DB_PASSWORD}" LABEL org.label-schema.schema-version="1.0" \ - org.label-schema.build-date=$DATE \ + org.label-schema.build-date=${DATE} \ org.label-schema.vendor="inventree" \ org.label-schema.name="inventree/inventree" \ org.label-schema.url="https://hub.docker.com/r/inventree/inventree-docker" \ - org.label-schema.version=$INVENTREE_VERSION \ - org.label-schema.vcs-url=$INVENTREE_REPO \ - org.label-schema.vcs-branch=$BRANCH \ - org.label-schema.vcs-ref=$COMMIT + org.label-schema.version=${INVENTREE_VERSION} \ + org.label-schema.vcs-url=${INVENTREE_REPO} \ + org.label-schema.vcs-branch=${BRANCH} \ + org.label-schema.vcs-ref=${COMMIT} RUN echo "Installing InvenTree '${INVENTREE_VERSION}' from ${INVENTREE_REPO}" @@ -44,15 +61,15 @@ RUN apk add --no-cache postgresql-contrib postgresql-dev libpq RUN apk add --no-cache mariadb-connector-c mariadb-dev # Clone source code -RUN git clone --branch $INVENTREE_VERSION --depth 1 ${INVENTREE_REPO} ${INVENTREE_SRC_DIR} +RUN git clone --branch ${INVENTREE_VERSION} --depth 1 ${INVENTREE_REPO} ${INVENTREE_SRC_DIR} # Install required PIP packages -RUN python -m venv $INVENTREE_VENV && pip install --upgrade pip setuptools wheel -RUN python -m venv $INVENTREE_VENV && pip install --no-cache-dir -U invoke -RUN python -m venv $INVENTREE_VENV && pip install --no-cache-dir -U psycopg2 mysqlclient pgcli mariadb +RUN python -m venv ${INVENTREE_VENV} && pip install --upgrade pip setuptools wheel +RUN python -m venv ${INVENTREE_VENV} && pip install --no-cache-dir -U invoke +RUN python -m venv ${INVENTREE_VENV} && pip install --no-cache-dir -U psycopg2 mysqlclient pgcli mariadb # Install InvenTree packages -RUN python -m venv $INVENTREE_VENV && pip install --no-cache-dir -U -r $INVENTREE_SRC_DIR/requirements.txt +RUN python -m venv ${INVENTREE_VENV} && pip install --no-cache-dir -U -r ${INVENTREE_SRC_DIR}/requirements.txt # Install supervisor RUN apk add --no-cache supervisor @@ -61,6 +78,6 @@ RUN apk add --no-cache supervisor RUN mkdir /home/inventree/media /home/inventree/static /home/inventree/log /home/inventree/backup # Copy supervisor file -COPY deploy/inventree.conf /etc/supervisor/conf.d/inventree.conf +COPY docker/inventree.conf /etc/supervisor/conf.d/inventree.conf CMD ["/usr/bin/supervisord"] From d446f8ddd121f0fa0d6431b33beeab9be1917df8 Mon Sep 17 00:00:00 2001 From: Oliver Walters <oliver.henry.walters@gmail.com> Date: Thu, 1 Apr 2021 20:14:31 +1100 Subject: [PATCH 056/111] Add supervisor conf file specific to docker --- deploy/inventree.conf | 1 - docker/inventree.conf | 41 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+), 1 deletion(-) create mode 100644 docker/inventree.conf diff --git a/deploy/inventree.conf b/deploy/inventree.conf index a55c7163f6..782b6ae2f6 100644 --- a/deploy/inventree.conf +++ b/deploy/inventree.conf @@ -13,7 +13,6 @@ [supervisord] ; Change this path if log files are stored elsewhere logfile=/home/inventree/log/supervisor.log -nodaemon=true [program:inventree-server] user=inventree diff --git a/docker/inventree.conf b/docker/inventree.conf new file mode 100644 index 0000000000..dabf9ddafb --- /dev/null +++ b/docker/inventree.conf @@ -0,0 +1,41 @@ +; # Supervisor Config File (for docker build) +; +; This config file is specific to the InvenTree docker build! +; +; There are two separate processes which must be managed: +; +; ## Web Server +; The InvenTree server must be launched and managed as a process +; The recommended way to handle the web server is to use gunicorn +; +; ## Background Tasks +; A background task manager processes long-running and periodic tasks +; InvenTree uses django-q for this purpose + +[supervisord] +; Change this path if log files are stored elsewhere +logfile=/home/inventree/log/supervisor.log +nodaemon=true + +[program:inventree-server] +user=inventree +directory=/home/inventree/src/InvenTree +command=/home/inventree/env/bin/gunicorn -c gunicorn.conf.py InvenTree.wsgi -b 0.0.0.0:%(INVENTREE_PORT)s +startsecs=10 +autostart=true +autorestart=true +startretries=3 +; Change these paths if log files are stored elsewhere +stderr_logfile=/home/inventree/log/server.err.log +stdout_logfile=/home/inventree/log/server.out.log + +[program:inventree-cluster] +user=inventree +directory=/home/inventree/src/InvenTree +command=/home/inventree/env/bin/python manage.py qcluster +startsecs=10 +autostart=true +autorestart=true +; Change these paths if log files are stored elsewhere +stderr_logfile=/home/inventree/log/cluster.err.log +stdout_logfile=/home/inventree/log/cluster.out.log \ No newline at end of file From 839c29117dc516e2a4b76cc194d2da8c3fae3e31 Mon Sep 17 00:00:00 2001 From: Oliver Walters <oliver.henry.walters@gmail.com> Date: Thu, 1 Apr 2021 20:30:51 +1100 Subject: [PATCH 057/111] Dockerfile updates - Pipe supervisor logs to stdout (so they are passed to the docker instance) - Fix supervisor service - Expose home dir and port as env vars --- docker/Dockerfile | 8 ++++++-- docker/{inventree.conf => supervisor.conf} | 16 ++++++++-------- 2 files changed, 14 insertions(+), 10 deletions(-) rename docker/{inventree.conf => supervisor.conf} (69%) diff --git a/docker/Dockerfile b/docker/Dockerfile index ffba72f4c6..04d85184bd 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -16,6 +16,10 @@ ARG INVENTREE_DB_PASSWORD="" ENV PYTHONUNBUFFERED 1 +# InvenTree key settings +ENV INVENTREE_HOME="${INVENTREE_HOME}" +ENV INVENTREE_PORT="${INVENTREE_PORT}" + # InvenTree paths ENV INVENTREE_SRC_DIR="${INVENTREE_HOME}/src" ENV INVENTREE_STATIC_ROOT="${INVENTREE_HOME}/static" @@ -78,6 +82,6 @@ RUN apk add --no-cache supervisor RUN mkdir /home/inventree/media /home/inventree/static /home/inventree/log /home/inventree/backup # Copy supervisor file -COPY docker/inventree.conf /etc/supervisor/conf.d/inventree.conf +COPY docker/supervisor.conf /etc/supervisord.conf -CMD ["/usr/bin/supervisord"] +CMD ["/usr/bin/supervisord", "-c", "/etc/supervisord.conf"] diff --git a/docker/inventree.conf b/docker/supervisor.conf similarity index 69% rename from docker/inventree.conf rename to docker/supervisor.conf index dabf9ddafb..72609e5bac 100644 --- a/docker/inventree.conf +++ b/docker/supervisor.conf @@ -13,8 +13,6 @@ ; InvenTree uses django-q for this purpose [supervisord] -; Change this path if log files are stored elsewhere -logfile=/home/inventree/log/supervisor.log nodaemon=true [program:inventree-server] @@ -25,9 +23,10 @@ startsecs=10 autostart=true autorestart=true startretries=3 -; Change these paths if log files are stored elsewhere -stderr_logfile=/home/inventree/log/server.err.log -stdout_logfile=/home/inventree/log/server.out.log +stdout_logfile=/dev/fd/1 +stdout_logfile_maxbytes=0 +stderr_logfile=/dev/fd/2 +stderr_logfile_maxbytes=0 [program:inventree-cluster] user=inventree @@ -36,6 +35,7 @@ command=/home/inventree/env/bin/python manage.py qcluster startsecs=10 autostart=true autorestart=true -; Change these paths if log files are stored elsewhere -stderr_logfile=/home/inventree/log/cluster.err.log -stdout_logfile=/home/inventree/log/cluster.out.log \ No newline at end of file +stdout_logfile=/dev/fd/1 +stdout_logfile_maxbytes=0 +stderr_logfile=/dev/fd/2 +stderr_logfile_maxbytes=0 \ No newline at end of file From 148600a9c4263627ec3eb1c3dbd8c98f1d7de7c3 Mon Sep 17 00:00:00 2001 From: Oliver Walters <oliver.henry.walters@gmail.com> Date: Thu, 1 Apr 2021 20:38:18 +1100 Subject: [PATCH 058/111] Copy gunicorn.conf.py --- docker/Dockerfile | 3 +++ docker/gunicorn.conf.py | 6 ++++++ docker/supervisor.conf | 2 +- 3 files changed, 10 insertions(+), 1 deletion(-) create mode 100644 docker/gunicorn.conf.py diff --git a/docker/Dockerfile b/docker/Dockerfile index 04d85184bd..d3517b1a05 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -84,4 +84,7 @@ RUN mkdir /home/inventree/media /home/inventree/static /home/inventree/log /home # Copy supervisor file COPY docker/supervisor.conf /etc/supervisord.conf +# Copy gunicorn config file +COPY docker/gunicorn.conf.py /home/inventree/gunicorn.conf.py + CMD ["/usr/bin/supervisord", "-c", "/etc/supervisord.conf"] diff --git a/docker/gunicorn.conf.py b/docker/gunicorn.conf.py new file mode 100644 index 0000000000..1071c2d745 --- /dev/null +++ b/docker/gunicorn.conf.py @@ -0,0 +1,6 @@ +import multiprocessing + +workers = multiprocessing.cpu_count() * 2 + 1 + +max_requests = 1000 +max_requests_jitter = 50 diff --git a/docker/supervisor.conf b/docker/supervisor.conf index 72609e5bac..47eeaf6c28 100644 --- a/docker/supervisor.conf +++ b/docker/supervisor.conf @@ -18,7 +18,7 @@ nodaemon=true [program:inventree-server] user=inventree directory=/home/inventree/src/InvenTree -command=/home/inventree/env/bin/gunicorn -c gunicorn.conf.py InvenTree.wsgi -b 0.0.0.0:%(INVENTREE_PORT)s +command=/home/inventree/env/bin/gunicorn -c /home/inventree/gunicorn.conf.py InvenTree.wsgi -b 0.0.0.0:%(ENV_INVENTREE_PORT)s startsecs=10 autostart=true autorestart=true From db858b3cfc9611f12f93dbc0bf71287b2ad99070 Mon Sep 17 00:00:00 2001 From: Oliver Walters <oliver.henry.walters@gmail.com> Date: Thu, 1 Apr 2021 20:44:13 +1100 Subject: [PATCH 059/111] Install packages inside venv --- docker/Dockerfile | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/docker/Dockerfile b/docker/Dockerfile index d3517b1a05..fe1877b99d 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -67,13 +67,18 @@ RUN apk add --no-cache mariadb-connector-c mariadb-dev # Clone source code RUN git clone --branch ${INVENTREE_VERSION} --depth 1 ${INVENTREE_REPO} ${INVENTREE_SRC_DIR} -# Install required PIP packages -RUN python -m venv ${INVENTREE_VENV} && pip install --upgrade pip setuptools wheel -RUN python -m venv ${INVENTREE_VENV} && pip install --no-cache-dir -U invoke -RUN python -m venv ${INVENTREE_VENV} && pip install --no-cache-dir -U psycopg2 mysqlclient pgcli mariadb +# Setup Python virtual environment +RUN apk add python3-venv +RUN python -m venv ${INVENTREE_VENV}} +RUN source ${INVENTREE_VENV}/bin/activate + +# Install required PIP packages (into the virtual environment!) +RUN pip install --upgrade pip setuptools wheel +RUN pip install --no-cache-dir -U invoke +RUN pip install --no-cache-dir -U psycopg2 mysqlclient pgcli mariadb # Install InvenTree packages -RUN python -m venv ${INVENTREE_VENV} && pip install --no-cache-dir -U -r ${INVENTREE_SRC_DIR}/requirements.txt +RUN pip install --no-cache-dir -U -r ${INVENTREE_SRC_DIR}/requirements.txt # Install supervisor RUN apk add --no-cache supervisor From 47ba0599eb2467239f6bb95ac050ae9cdbd227ca Mon Sep 17 00:00:00 2001 From: Oliver Walters <oliver.henry.walters@gmail.com> Date: Thu, 1 Apr 2021 20:44:27 +1100 Subject: [PATCH 060/111] Reference environment variables in supervisor conf file --- docker/supervisor.conf | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docker/supervisor.conf b/docker/supervisor.conf index 47eeaf6c28..363bbe6b4a 100644 --- a/docker/supervisor.conf +++ b/docker/supervisor.conf @@ -17,8 +17,8 @@ nodaemon=true [program:inventree-server] user=inventree -directory=/home/inventree/src/InvenTree -command=/home/inventree/env/bin/gunicorn -c /home/inventree/gunicorn.conf.py InvenTree.wsgi -b 0.0.0.0:%(ENV_INVENTREE_PORT)s +directory=%(ENV_INVENTREE_SRC_DIR)s/InvenTree +command=%(ENV_INVENTREE_VENV)s/bin/gunicorn -c %(ENV_INVENTREE_HOME)s/gunicorn.conf.py InvenTree.wsgi -b 0.0.0.0:%(ENV_INVENTREE_PORT)s startsecs=10 autostart=true autorestart=true @@ -30,8 +30,8 @@ stderr_logfile_maxbytes=0 [program:inventree-cluster] user=inventree -directory=/home/inventree/src/InvenTree -command=/home/inventree/env/bin/python manage.py qcluster +directory=%(ENV_INVENTREE_SRC_DIR)s/InvenTree +command=%(ENV_INVENTREE_VENV)s/bin/python manage.py qcluster startsecs=10 autostart=true autorestart=true From 8e7e36089b4eedf95f5ae3491bac9c5c0428f4cf Mon Sep 17 00:00:00 2001 From: Oliver Walters <oliver.henry.walters@gmail.com> Date: Thu, 1 Apr 2021 21:11:59 +1100 Subject: [PATCH 061/111] Fix venv --- docker/Dockerfile | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/docker/Dockerfile b/docker/Dockerfile index fe1877b99d..4d59ec4b8e 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -1,9 +1,10 @@ FROM python:alpine as production -# Configuration params +# GitHub source ARG INVENTREE_REPO="https://github.com/inventree/InvenTree.git" ARG INVENTREE_VERSION="master" -ARG INVENTREE_HOME="/home/inventree" + +# InvenTree server port ARG INVENTREE_PORT="80" # Database configuration options @@ -17,7 +18,7 @@ ARG INVENTREE_DB_PASSWORD="" ENV PYTHONUNBUFFERED 1 # InvenTree key settings -ENV INVENTREE_HOME="${INVENTREE_HOME}" +ENV INVENTREE_HOME="/home/inventree" ENV INVENTREE_PORT="${INVENTREE_PORT}" # InvenTree paths @@ -50,7 +51,7 @@ RUN echo "Installing InvenTree '${INVENTREE_VERSION}' from ${INVENTREE_REPO}" # Create user account RUN addgroup -S inventreegroup && adduser -S inventree -G inventreegroup -WORKDIR /home/inventree +WORKDIR ${INVENTREE_HOME} # Install required system packages RUN apk add --no-cache git make bash \ @@ -68,28 +69,27 @@ RUN apk add --no-cache mariadb-connector-c mariadb-dev RUN git clone --branch ${INVENTREE_VERSION} --depth 1 ${INVENTREE_REPO} ${INVENTREE_SRC_DIR} # Setup Python virtual environment -RUN apk add python3-venv -RUN python -m venv ${INVENTREE_VENV}} -RUN source ${INVENTREE_VENV}/bin/activate +RUN python3 -m venv ${INVENTREE_VENV} # Install required PIP packages (into the virtual environment!) -RUN pip install --upgrade pip setuptools wheel -RUN pip install --no-cache-dir -U invoke -RUN pip install --no-cache-dir -U psycopg2 mysqlclient pgcli mariadb +RUN source ${INVENTREE_VENV}/bin/activate && pip install --upgrade pip setuptools wheel +RUN source ${INVENTREE_VENV}/bin/activate && pip install --no-cache-dir -U invoke +RUN source ${INVENTREE_VENV}/bin/activate && pip install --no-cache-dir -U psycopg2 mysqlclient pgcli mariadb +RUN source ${INVENTREE_VENV}/bin/activate && pip install --no-cache-dir -U gunicorn # Install InvenTree packages -RUN pip install --no-cache-dir -U -r ${INVENTREE_SRC_DIR}/requirements.txt +RUN source ${INVENTREE_VENV}/bin/activate && pip install --no-cache-dir -U -r ${INVENTREE_SRC_DIR}/requirements.txt # Install supervisor RUN apk add --no-cache supervisor # Create required directories -RUN mkdir /home/inventree/media /home/inventree/static /home/inventree/log /home/inventree/backup +RUN mkdir ${INVENTREE_HOME}/media ${INVENTREE_HOME}/static ${INVENTREE_HOME}/backup # Copy supervisor file COPY docker/supervisor.conf /etc/supervisord.conf # Copy gunicorn config file -COPY docker/gunicorn.conf.py /home/inventree/gunicorn.conf.py +COPY docker/gunicorn.conf.py ${INVENTREE_HOME}/gunicorn.conf.py CMD ["/usr/bin/supervisord", "-c", "/etc/supervisord.conf"] From be41be3981858e182bb9d7029a09496f305ee94d Mon Sep 17 00:00:00 2001 From: Oliver Walters <oliver.henry.walters@gmail.com> Date: Fri, 2 Apr 2021 00:03:56 +1100 Subject: [PATCH 062/111] Add "wait_for_db" management command --- .../management/commands/wait_for_db.py | 31 +++++++++++++++++++ InvenTree/config_template.yaml | 5 --- docker/Dockerfile | 1 + 3 files changed, 32 insertions(+), 5 deletions(-) create mode 100644 InvenTree/InvenTree/management/commands/wait_for_db.py diff --git a/InvenTree/InvenTree/management/commands/wait_for_db.py b/InvenTree/InvenTree/management/commands/wait_for_db.py new file mode 100644 index 0000000000..5d152d26ca --- /dev/null +++ b/InvenTree/InvenTree/management/commands/wait_for_db.py @@ -0,0 +1,31 @@ +""" +Custom management command, wait for the database to be ready! +""" + +from django.core.management.base import BaseCommand + +from django.db import connections +from django.db.utils import OperationalError + +import time + + +class Command(BaseCommand): + """ + django command to pause execution until the database is ready + """ + + def handle(self, *args, **kwargs): + self.stdout.write("Waiting for database...") + + db_conn = None + + while not db_conn: + try: + # get the database with keyword 'default' from settings.py + db_conn = connections['default'] + # prints success messge in green + self.stdout.write(self.style.SUCCESS('InvenTree database connected')) + except OperationalError: + self.stdout.write("Database unavailable, waiting 5 seconds ...") + time.sleep(5) diff --git a/InvenTree/config_template.yaml b/InvenTree/config_template.yaml index bab673306f..7ce62e887d 100644 --- a/InvenTree/config_template.yaml +++ b/InvenTree/config_template.yaml @@ -9,11 +9,6 @@ database: # Uncomment (and edit) one of the database configurations below, # or specify database options using environment variables - - # Default installation uses a simple sqlite database - # For production, consider changing this! - ENGINE: sqlite3 - NAME: '/home/inventree/database.sqlite3' # Refer to the django documentation for full list of options diff --git a/docker/Dockerfile b/docker/Dockerfile index 4d59ec4b8e..c49c6e53f0 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -23,6 +23,7 @@ ENV INVENTREE_PORT="${INVENTREE_PORT}" # InvenTree paths ENV INVENTREE_SRC_DIR="${INVENTREE_HOME}/src" +ENV INVENTREE_MNG_DIR="${INVENTREE_SRC_DIR}/InvenTree" ENV INVENTREE_STATIC_ROOT="${INVENTREE_HOME}/static" ENV INVENTREE_MEDIA_ROOT="${INVENTREE_HOME}/media" ENV INVENTREE_LOG_DIR="${INVENTREE_HOME}/log" From 8d3b9e2ca4fd6137660a04e70a9804bb5d48adbf Mon Sep 17 00:00:00 2001 From: Oliver Walters <oliver.henry.walters@gmail.com> Date: Fri, 2 Apr 2021 00:06:17 +1100 Subject: [PATCH 063/111] Updates to settings.py - Create secret_key.txt if it does not exist - Copy default settings file if it does not exist --- InvenTree/InvenTree/settings.py | 31 ++++++++++++++++++++++++++++--- tasks.py | 32 +++++++------------------------- 2 files changed, 35 insertions(+), 28 deletions(-) diff --git a/InvenTree/InvenTree/settings.py b/InvenTree/InvenTree/settings.py index eee69c0780..3f705ae70c 100644 --- a/InvenTree/InvenTree/settings.py +++ b/InvenTree/InvenTree/settings.py @@ -13,6 +13,9 @@ database setup in this file. import logging import os +import random +import string +import shutil import sys import tempfile from datetime import datetime @@ -55,8 +58,10 @@ BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) cfg_filename = os.path.join(BASE_DIR, 'config.yaml') if not os.path.exists(cfg_filename): - print("Error: config.yaml not found") - sys.exit(-1) + print("InvenTree configuration file 'config.yaml' not found - creating default file") + + cfg_template = os.path.join(BASE_DIR, "config_template.yaml") + shutil.copyfile(cfg_template, cfg_filename) with open(cfg_filename, 'r') as cfg: CONFIG = yaml.safe_load(cfg) @@ -99,6 +104,17 @@ LOGGING = { # Get a logger instance for this setup file logger = logging.getLogger(__name__) +""" +Specify a secret key to be used by django. + +Following options are tested, in descending order of preference: + +A) Check for environment variable INVENTREE_SECRET_KEY => Use raw key data +B) Check for environment variable INVENTREE_SECRET_KEY_FILE => Load key data from file +C) Look for default key file "secret_key.txt" +d) Create "secret_key.txt" if it does not exist +""" + if os.getenv("INVENTREE_SECRET_KEY"): # Secret key passed in directly SECRET_KEY = os.getenv("INVENTREE_SECRET_KEY").strip() @@ -111,11 +127,20 @@ else: if os.path.isfile(key_file): logger.info("SECRET_KEY loaded by INVENTREE_SECRET_KEY_FILE") else: - logger.error(f"Secret key file {key_file} not found") + logger.error(f"Secret key file '{key_file}'' not found") exit(-1) else: # default secret key location key_file = os.path.join(BASE_DIR, "secret_key.txt") + + if not os.path.exists(key_file): + logger.info("Creating key file 'secret_key.txt'") + # Create a random key file + with open(key_file, 'w') as f: + options = string.digits + string.ascii_letters + string.punctuation + key = ''.join([random.choice(options) for i in range(50)]) + f.write(key) + logger.info(f"SECRET_KEY loaded from {key_file}") try: SECRET_KEY = open(key_file, "r").read().strip() diff --git a/tasks.py b/tasks.py index 2d7d395d10..83a99949f3 100644 --- a/tasks.py +++ b/tasks.py @@ -3,11 +3,10 @@ from invoke import task from shutil import copyfile -import random -import string import os import sys + def apps(): """ Returns a list of installed apps @@ -27,6 +26,7 @@ def apps(): 'users', ] + def localDir(): """ Returns the directory of *THIS* file. @@ -35,6 +35,7 @@ def localDir(): """ return os.path.dirname(os.path.abspath(__file__)) + def managePyDir(): """ Returns the directory of the manage.py file @@ -42,6 +43,7 @@ def managePyDir(): return os.path.join(localDir(), 'InvenTree') + def managePyPath(): """ Return the path of the manage.py file @@ -49,6 +51,7 @@ def managePyPath(): return os.path.join(managePyDir(), 'manage.py') + def manage(c, cmd, pty=False): """ Runs a given command against django's "manage.py" script. @@ -63,32 +66,11 @@ def manage(c, cmd, pty=False): cmd=cmd ), pty=pty) -@task(help={'length': 'Length of secret key (default=50)'}) -def key(c, length=50, force=False): - """ - Generates a SECRET_KEY file which InvenTree uses for generating security hashes - """ - SECRET_KEY_FILE = os.path.join(localDir(), 'InvenTree', 'secret_key.txt') - - # If a SECRET_KEY file does not exist, generate a new one! - if force or not os.path.exists(SECRET_KEY_FILE): - print("Generating SECRET_KEY file - " + SECRET_KEY_FILE) - with open(SECRET_KEY_FILE, 'w') as key_file: - options = string.digits + string.ascii_letters + string.punctuation - - key = ''.join([random.choice(options) for i in range(length)]) - - key_file.write(key) - - else: - print("SECRET_KEY file already exists - skipping") - - -@task(post=[key]) +@task def install(c): """ - Installs required python packages, and runs initial setup functions. + Installs required python packages """ # Install required Python packages with PIP From 2436b1f2c985a36abf72dde35bd365790ed184db Mon Sep 17 00:00:00 2001 From: Oliver Walters <oliver.henry.walters@gmail.com> Date: Fri, 2 Apr 2021 00:40:47 +1100 Subject: [PATCH 064/111] Entrypoint script - start.sh --- InvenTree/config_template.yaml | 4 ++-- docker/Dockerfile | 18 ++++++++++++++--- docker/start.sh | 35 ++++++++++++++++++++++++++++++++++ tasks.py | 2 +- 4 files changed, 53 insertions(+), 6 deletions(-) create mode 100644 docker/start.sh diff --git a/InvenTree/config_template.yaml b/InvenTree/config_template.yaml index 7ce62e887d..fb30f2d339 100644 --- a/InvenTree/config_template.yaml +++ b/InvenTree/config_template.yaml @@ -28,7 +28,7 @@ database: # NAME: '/home/inventree/database.sqlite3' # --- Example Configuration - MySQL --- - #ENGINE: django.db.backends.mysql + #ENGINE: mysql #NAME: inventree #USER: inventree #PASSWORD: inventree_password @@ -36,7 +36,7 @@ database: #PORT: '3306' # --- Example Configuration - Postgresql --- - #ENGINE: django.db.backends.postgresql + #ENGINE: postgresql #NAME: inventree #USER: inventree #PASSWORD: inventree_password diff --git a/docker/Dockerfile b/docker/Dockerfile index c49c6e53f0..a9eaef612b 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -3,12 +3,13 @@ FROM python:alpine as production # GitHub source ARG INVENTREE_REPO="https://github.com/inventree/InvenTree.git" ARG INVENTREE_VERSION="master" +ARG INVENTREE_CONFIG_FILE="InvenTree/config_template.yaml" # InvenTree server port ARG INVENTREE_PORT="80" # Database configuration options -ARG INVENTREE_DB_ENGINE="sqlite" +ARG INVENTREE_DB_ENGINE="sqlite3" ARG INVENTREE_DB_NAME="inventree_db.sqlite3" ARG INVENTREE_DB_HOST="127.0.0.1" ARG INVENTREE_DB_PORT="" @@ -21,6 +22,8 @@ ENV PYTHONUNBUFFERED 1 ENV INVENTREE_HOME="/home/inventree" ENV INVENTREE_PORT="${INVENTREE_PORT}" +ENV INVENTREE_LOG_LEVEL="INFO" + # InvenTree paths ENV INVENTREE_SRC_DIR="${INVENTREE_HOME}/src" ENV INVENTREE_MNG_DIR="${INVENTREE_SRC_DIR}/InvenTree" @@ -42,7 +45,7 @@ LABEL org.label-schema.schema-version="1.0" \ org.label-schema.build-date=${DATE} \ org.label-schema.vendor="inventree" \ org.label-schema.name="inventree/inventree" \ - org.label-schema.url="https://hub.docker.com/r/inventree/inventree-docker" \ + org.label-schema.url="https://hub.docker.com/r/inventree/inventree" \ org.label-schema.version=${INVENTREE_VERSION} \ org.label-schema.vcs-url=${INVENTREE_REPO} \ org.label-schema.vcs-branch=${BRANCH} \ @@ -93,4 +96,13 @@ COPY docker/supervisor.conf /etc/supervisord.conf # Copy gunicorn config file COPY docker/gunicorn.conf.py ${INVENTREE_HOME}/gunicorn.conf.py -CMD ["/usr/bin/supervisord", "-c", "/etc/supervisord.conf"] +# Copy default InvenTree config file +COPY ${INVENTREE_CONFIG_FILE} ${INVENTREE_SRC_DIR}/InvenTree/config.yaml + +# Copy startup script +COPY docker/start.sh ${INVENTREE_HOME}/start.sh + +RUN chmod 755 ${INVENTREE_HOME}/start.sh + +# Let us begin +CMD "./start.sh" diff --git a/docker/start.sh b/docker/start.sh new file mode 100644 index 0000000000..3e1287527b --- /dev/null +++ b/docker/start.sh @@ -0,0 +1,35 @@ +#!/bin/sh + +# Check that the database engine is specified +if [ -z "$INVENTREE_DB_ENGINE" ]; then + echo "INVENTREE_DB_ENGINE not configured" + exit 1 +fi + +# Check that the base dir is set +if [ -z "$INVENTREE_HOME" ]; then + echo "INVENTREE_HOME not configured" + exit 1 +fi + +# Activate virtual environment +source $INVENTREE_VENV/bin/activate + +# Wait for the database to be ready +cd $INVENTREE_MNG_DIR +python manage.py wait_for_db + +sleep 10 + +echo "Running InvenTree database migrations and collecting static files..." + +# We assume at this stage that the database is up and running +# Ensure that the database schema are up to date +python manage.py check || exit 1 +python manage.py migrate --noinput || exit 1 +python manage.py migrate --run-syncdb || exit 1 +python manage.py collectstatic --noinput || exit 1 +python manage.py clearsessions || exit 1 + +# Now we can launch the server and background worker +/usr/bin/supervisord -c /etc/supervisord.conf diff --git a/tasks.py b/tasks.py index 83a99949f3..c6d7dd0173 100644 --- a/tasks.py +++ b/tasks.py @@ -143,7 +143,7 @@ def static(c): as per Django requirements. """ - manage(c, "collectstatic") + manage(c, "collectstatic --no-input") @task(pre=[install, migrate, static]) From 00c4519d2863a6d801ad12ad17de549b605d36c7 Mon Sep 17 00:00:00 2001 From: Oliver Walters <oliver.henry.walters@gmail.com> Date: Fri, 2 Apr 2021 00:54:29 +1100 Subject: [PATCH 065/111] Simplify dockerfile --- docker/Dockerfile | 12 ------------ docker/supervisor.conf | 2 +- 2 files changed, 1 insertion(+), 13 deletions(-) diff --git a/docker/Dockerfile b/docker/Dockerfile index a9eaef612b..99131700ee 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -5,22 +5,10 @@ ARG INVENTREE_REPO="https://github.com/inventree/InvenTree.git" ARG INVENTREE_VERSION="master" ARG INVENTREE_CONFIG_FILE="InvenTree/config_template.yaml" -# InvenTree server port -ARG INVENTREE_PORT="80" - -# Database configuration options -ARG INVENTREE_DB_ENGINE="sqlite3" -ARG INVENTREE_DB_NAME="inventree_db.sqlite3" -ARG INVENTREE_DB_HOST="127.0.0.1" -ARG INVENTREE_DB_PORT="" -ARG INVENTREE_DB_USER="" -ARG INVENTREE_DB_PASSWORD="" - ENV PYTHONUNBUFFERED 1 # InvenTree key settings ENV INVENTREE_HOME="/home/inventree" -ENV INVENTREE_PORT="${INVENTREE_PORT}" ENV INVENTREE_LOG_LEVEL="INFO" diff --git a/docker/supervisor.conf b/docker/supervisor.conf index 363bbe6b4a..554200837d 100644 --- a/docker/supervisor.conf +++ b/docker/supervisor.conf @@ -18,7 +18,7 @@ nodaemon=true [program:inventree-server] user=inventree directory=%(ENV_INVENTREE_SRC_DIR)s/InvenTree -command=%(ENV_INVENTREE_VENV)s/bin/gunicorn -c %(ENV_INVENTREE_HOME)s/gunicorn.conf.py InvenTree.wsgi -b 0.0.0.0:%(ENV_INVENTREE_PORT)s +command=%(ENV_INVENTREE_VENV)s/bin/gunicorn -c %(ENV_INVENTREE_HOME)s/gunicorn.conf.py InvenTree.wsgi -b 0.0.0.0:80 startsecs=10 autostart=true autorestart=true From d91531720ba5e503efbc7242aff2b74e81937633 Mon Sep 17 00:00:00 2001 From: Oliver Walters <oliver.henry.walters@gmail.com> Date: Wed, 7 Apr 2021 22:17:24 +1000 Subject: [PATCH 066/111] Unit testing for task scheduling --- InvenTree/InvenTree/tasks.py | 10 ++++--- InvenTree/InvenTree/test_tasks.py | 43 +++++++++++++++++++++++++++++++ 2 files changed, 50 insertions(+), 3 deletions(-) create mode 100644 InvenTree/InvenTree/test_tasks.py diff --git a/InvenTree/InvenTree/tasks.py b/InvenTree/InvenTree/tasks.py index eba145c660..c12d0ec4df 100644 --- a/InvenTree/InvenTree/tasks.py +++ b/InvenTree/InvenTree/tasks.py @@ -23,6 +23,7 @@ def schedule_task(taskname, **kwargs): # If unspecified, repeat indefinitely repeats = kwargs.pop('repeats', -1) + kwargs['repeats'] = repeats try: from django_q.models import Schedule @@ -31,15 +32,18 @@ def schedule_task(taskname, **kwargs): return try: + # If this task is already scheduled, don't schedule it again + # Instead, update the scheduling parameters if Schedule.objects.filter(func=taskname).exists(): - logger.info(f"Scheduled task '{taskname}' already exists. (Skipping)") + logger.info(f"Scheduled task '{taskname}' already exists - updating!") + + Schedule.objects.filter(func=taskname).update(**kwargs) else: logger.info(f"Creating scheduled task '{taskname}'") Schedule.objects.create( name=taskname, func=taskname, - repeats=repeats, **kwargs ) except (OperationalError, ProgrammingError): @@ -82,8 +86,8 @@ def delete_successful_tasks(): try: from django_q.models import Success - logger.warning("Could not perform 'delete_successful_tasks' - App registry not ready") except AppRegistryNotReady: + logger.warning("Could not perform 'delete_successful_tasks' - App registry not ready") return threshold = datetime.now() - timedelta(days=30) diff --git a/InvenTree/InvenTree/test_tasks.py b/InvenTree/InvenTree/test_tasks.py new file mode 100644 index 0000000000..02e8d14e5e --- /dev/null +++ b/InvenTree/InvenTree/test_tasks.py @@ -0,0 +1,43 @@ +""" +Unit tests for task management +""" + +from django.test import TestCase +from django_q.models import Schedule + +import InvenTree.tasks + + +class ScheduledTaskTests(TestCase): + """ + Unit tests for scheduled tasks + """ + + def get_tasks(self, name): + + return Schedule.objects.filter(func=name) + + def test_add_task(self): + """ + Ensure that duplicate tasks cannot be added. + """ + + task = 'InvenTree.tasks.heartbeat' + + self.assertEqual(self.get_tasks(task).count(), 0) + + InvenTree.tasks.schedule_task(task, schedule_type=Schedule.MINUTES, minutes=10) + + self.assertEqual(self.get_tasks(task).count(), 1) + + t = Schedule.objects.get(func=task) + + self.assertEqual(t.minutes, 10) + + # Attempt to schedule the same task again + InvenTree.tasks.schedule_task(task, schedule_type=Schedule.MINUTES, minutes=5) + self.assertEqual(self.get_tasks(task).count(), 1) + + # But the 'minutes' should have been updated + t = Schedule.objects.get(func=task) + self.assertEqual(t.minutes, 5) From 4a3ca4638c6ec1e4da9359c5a6a316073ff64e09 Mon Sep 17 00:00:00 2001 From: Oliver Walters <oliver.henry.walters@gmail.com> Date: Wed, 7 Apr 2021 22:27:55 +1000 Subject: [PATCH 067/111] Dockerfile updates --- InvenTree/InvenTree/management/commands/wait_for_db.py | 2 +- docker/Dockerfile | 2 +- docker/start.sh | 4 +++- docker/{supervisor.conf => supervisord.conf} | 3 ++- 4 files changed, 7 insertions(+), 4 deletions(-) rename docker/{supervisor.conf => supervisord.conf} (88%) diff --git a/InvenTree/InvenTree/management/commands/wait_for_db.py b/InvenTree/InvenTree/management/commands/wait_for_db.py index 5d152d26ca..9019a168ef 100644 --- a/InvenTree/InvenTree/management/commands/wait_for_db.py +++ b/InvenTree/InvenTree/management/commands/wait_for_db.py @@ -27,5 +27,5 @@ class Command(BaseCommand): # prints success messge in green self.stdout.write(self.style.SUCCESS('InvenTree database connected')) except OperationalError: - self.stdout.write("Database unavailable, waiting 5 seconds ...") + self.stdout.write(self.style.ERROR("Database unavailable, waiting 5 seconds ...")) time.sleep(5) diff --git a/docker/Dockerfile b/docker/Dockerfile index 99131700ee..3eb62af93b 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -79,7 +79,7 @@ RUN apk add --no-cache supervisor RUN mkdir ${INVENTREE_HOME}/media ${INVENTREE_HOME}/static ${INVENTREE_HOME}/backup # Copy supervisor file -COPY docker/supervisor.conf /etc/supervisord.conf +COPY docker/supervisord.conf ${INVENTREE_HOME}/supervisord.conf # Copy gunicorn config file COPY docker/gunicorn.conf.py ${INVENTREE_HOME}/gunicorn.conf.py diff --git a/docker/start.sh b/docker/start.sh index 3e1287527b..21334d812a 100644 --- a/docker/start.sh +++ b/docker/start.sh @@ -15,6 +15,8 @@ fi # Activate virtual environment source $INVENTREE_VENV/bin/activate +sleep 5 + # Wait for the database to be ready cd $INVENTREE_MNG_DIR python manage.py wait_for_db @@ -32,4 +34,4 @@ python manage.py collectstatic --noinput || exit 1 python manage.py clearsessions || exit 1 # Now we can launch the server and background worker -/usr/bin/supervisord -c /etc/supervisord.conf +/usr/bin/supervisord -c $INVENTREE_HOME/supervisord.conf diff --git a/docker/supervisor.conf b/docker/supervisord.conf similarity index 88% rename from docker/supervisor.conf rename to docker/supervisord.conf index 554200837d..e0eaad4cfb 100644 --- a/docker/supervisor.conf +++ b/docker/supervisord.conf @@ -13,6 +13,7 @@ ; InvenTree uses django-q for this purpose [supervisord] +user=inventree nodaemon=true [program:inventree-server] @@ -31,7 +32,7 @@ stderr_logfile_maxbytes=0 [program:inventree-cluster] user=inventree directory=%(ENV_INVENTREE_SRC_DIR)s/InvenTree -command=%(ENV_INVENTREE_VENV)s/bin/python manage.py qcluster +command=%(ENV_INVENTREE_VENV)s/bin/python DJANGO_SETTINGS_FILE=InvenTree.settings manage.py qcluster startsecs=10 autostart=true autorestart=true From d4d9263131b139e5aa63cfe2d0d2c748f1312f70 Mon Sep 17 00:00:00 2001 From: Oliver Walters <oliver.henry.walters@gmail.com> Date: Wed, 7 Apr 2021 23:46:03 +1000 Subject: [PATCH 068/111] Add option to specify config file via environment variable --- InvenTree/InvenTree/settings.py | 16 +++++++++++++++- docker/Dockerfile | 12 ++++-------- docker/start.sh | 10 +++------- docker/supervisord.conf | 2 +- 4 files changed, 23 insertions(+), 17 deletions(-) diff --git a/InvenTree/InvenTree/settings.py b/InvenTree/InvenTree/settings.py index 3f705ae70c..be9a4b729f 100644 --- a/InvenTree/InvenTree/settings.py +++ b/InvenTree/InvenTree/settings.py @@ -55,7 +55,21 @@ TESTING = 'test' in sys.argv # Build paths inside the project like this: os.path.join(BASE_DIR, ...) BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) -cfg_filename = os.path.join(BASE_DIR, 'config.yaml') +# Specify where the "config file" is located. +# By default, this is 'config.yaml' + +cfg_filename = os.getenv('INVENTREE_CONFIG_FILE') + +if cfg_filename: + cfg_filename = cfg_filename.strip() + cfg_filename = os.path.abspath(cfg_filename) + + if not os.path.exists(cfg_filename): + print(f"InvenTree configuration file '{cfg_filename}' does not exist!") + sys.exit(1) +else: + # Config file is *not* specified - use the default + cfg_filename = os.path.join(BASE_DIR, 'config.yaml') if not os.path.exists(cfg_filename): print("InvenTree configuration file 'config.yaml' not found - creating default file") diff --git a/docker/Dockerfile b/docker/Dockerfile index 3eb62af93b..073698adcd 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -3,7 +3,6 @@ FROM python:alpine as production # GitHub source ARG INVENTREE_REPO="https://github.com/inventree/InvenTree.git" ARG INVENTREE_VERSION="master" -ARG INVENTREE_CONFIG_FILE="InvenTree/config_template.yaml" ENV PYTHONUNBUFFERED 1 @@ -79,18 +78,15 @@ RUN apk add --no-cache supervisor RUN mkdir ${INVENTREE_HOME}/media ${INVENTREE_HOME}/static ${INVENTREE_HOME}/backup # Copy supervisor file -COPY docker/supervisord.conf ${INVENTREE_HOME}/supervisord.conf +COPY supervisord.conf ${INVENTREE_HOME}/supervisord.conf # Copy gunicorn config file -COPY docker/gunicorn.conf.py ${INVENTREE_HOME}/gunicorn.conf.py - -# Copy default InvenTree config file -COPY ${INVENTREE_CONFIG_FILE} ${INVENTREE_SRC_DIR}/InvenTree/config.yaml +COPY gunicorn.conf.py ${INVENTREE_HOME}/gunicorn.conf.py # Copy startup script -COPY docker/start.sh ${INVENTREE_HOME}/start.sh +COPY start.sh ${INVENTREE_HOME}/start.sh RUN chmod 755 ${INVENTREE_HOME}/start.sh # Let us begin -CMD "./start.sh" +CMD ["bash", "./start.sh"] diff --git a/docker/start.sh b/docker/start.sh index 21334d812a..f17150d0ca 100644 --- a/docker/start.sh +++ b/docker/start.sh @@ -1,17 +1,13 @@ #!/bin/sh +echo "Starting InvenTree server..." + # Check that the database engine is specified if [ -z "$INVENTREE_DB_ENGINE" ]; then echo "INVENTREE_DB_ENGINE not configured" exit 1 fi -# Check that the base dir is set -if [ -z "$INVENTREE_HOME" ]; then - echo "INVENTREE_HOME not configured" - exit 1 -fi - # Activate virtual environment source $INVENTREE_VENV/bin/activate @@ -34,4 +30,4 @@ python manage.py collectstatic --noinput || exit 1 python manage.py clearsessions || exit 1 # Now we can launch the server and background worker -/usr/bin/supervisord -c $INVENTREE_HOME/supervisord.conf +/usr/bin/supervisord -c $INVENTREE_HOME/supervisord.conf \ No newline at end of file diff --git a/docker/supervisord.conf b/docker/supervisord.conf index e0eaad4cfb..8dc8e77fc1 100644 --- a/docker/supervisord.conf +++ b/docker/supervisord.conf @@ -19,7 +19,7 @@ nodaemon=true [program:inventree-server] user=inventree directory=%(ENV_INVENTREE_SRC_DIR)s/InvenTree -command=%(ENV_INVENTREE_VENV)s/bin/gunicorn -c %(ENV_INVENTREE_HOME)s/gunicorn.conf.py InvenTree.wsgi -b 0.0.0.0:80 +command=%(ENV_INVENTREE_VENV)s/bin/gunicorn -c %(ENV_INVENTREE_HOME)s/gunicorn.conf.py InvenTree.wsgi -b 0.0.0.0:8080 startsecs=10 autostart=true autorestart=true From 14aead038e2ec49d71689511a40112c9e9805e30 Mon Sep 17 00:00:00 2001 From: Oliver Walters <oliver.henry.walters@gmail.com> Date: Wed, 7 Apr 2021 23:46:30 +1000 Subject: [PATCH 069/111] Adds docker_compose file --- docker/docker-compose.yml | 49 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 docker/docker-compose.yml diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml new file mode 100644 index 0000000000..abf57c24a7 --- /dev/null +++ b/docker/docker-compose.yml @@ -0,0 +1,49 @@ +version: "3.8" + +# Docker compose recipe for InvenTree +# - Runs PostgreSQL as the database backend +# - Serves web data using Gunicorn + +services: + # Use PostgreSQL as the database backend + # Note: this can be changed to a different backend, + # just make sure that you change the INVENTREE_DB_xxx vars below + db: + image: postgres + container_name: db + ports: + - 5432/tcp + environment: + - PGDATA=/var/lib/postgresql/data/pgdb + - POSTGRES_USER=pguser + - POSTGRES_PASSWORD=pgpassword + volumes: + - database_data:/var/lib/postgresql/data/ + restart: unless-stopped + + inventree: + build: . + image: inventree/inventree:latest + container_name: inventree + ports: + - 8080:8080 + depends_on: + - db + volumes: + - static_volume:/home/inventree/static + - media_volume:/home/inventree/media + - backup_volume:/home/inventree/backup + environment: + - INVENTREE_DB_ENGINE=postgresql + - INVENTREE_DB_NAME=inventree + - INVENTREE_DB_USER=pguser + - INVENTREE_DB_PASSWORD=pgpassword + - INVENTREE_DB_PORT=5432 + - INVENTREE_DB_HOST=db + restart: unless-stopped + +volumes: + database_data: + static_volume: + media_volume: + backup_volume: From ed304f571a78a839018154b120a7081ab3b92fa9 Mon Sep 17 00:00:00 2001 From: Oliver Walters <oliver.henry.walters@gmail.com> Date: Thu, 8 Apr 2021 00:05:37 +1000 Subject: [PATCH 070/111] Better configuration of github repo --- .github/workflows/docker.yaml | 2 +- InvenTree/InvenTree/management/commands/wait_for_db.py | 4 +++- docker/Dockerfile | 10 +++++++--- 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/.github/workflows/docker.yaml b/.github/workflows/docker.yaml index 749616d716..34ca585c98 100644 --- a/.github/workflows/docker.yaml +++ b/.github/workflows/docker.yaml @@ -12,5 +12,5 @@ jobs: steps: - uses: actions/checkout@v2 - name: Build Docker Image - run: docker build . --file docker/Dockerfile --tag inventree:$(date +%s) + run: cd docker && docker build . --tag inventree:$(date +%s) \ No newline at end of file diff --git a/InvenTree/InvenTree/management/commands/wait_for_db.py b/InvenTree/InvenTree/management/commands/wait_for_db.py index 9019a168ef..198c5dcf73 100644 --- a/InvenTree/InvenTree/management/commands/wait_for_db.py +++ b/InvenTree/InvenTree/management/commands/wait_for_db.py @@ -7,6 +7,8 @@ from django.core.management.base import BaseCommand from django.db import connections from django.db.utils import OperationalError +import psycopg2 + import time @@ -26,6 +28,6 @@ class Command(BaseCommand): db_conn = connections['default'] # prints success messge in green self.stdout.write(self.style.SUCCESS('InvenTree database connected')) - except OperationalError: + except (OperationalError, psycopg2.OperationalError): self.stdout.write(self.style.ERROR("Database unavailable, waiting 5 seconds ...")) time.sleep(5) diff --git a/docker/Dockerfile b/docker/Dockerfile index 073698adcd..a86fd09e45 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -1,14 +1,18 @@ FROM python:alpine as production # GitHub source -ARG INVENTREE_REPO="https://github.com/inventree/InvenTree.git" -ARG INVENTREE_VERSION="master" +ARG repository="https://github.com/inventree/InvenTree.git" +ARG branch="master" ENV PYTHONUNBUFFERED 1 # InvenTree key settings ENV INVENTREE_HOME="/home/inventree" +# GitHub settings +ENV INVENTREE_REPO="${repository}" +ENV INVENTREE_BRANCH="${branch}" + ENV INVENTREE_LOG_LEVEL="INFO" # InvenTree paths @@ -57,7 +61,7 @@ RUN apk add --no-cache postgresql-contrib postgresql-dev libpq RUN apk add --no-cache mariadb-connector-c mariadb-dev # Clone source code -RUN git clone --branch ${INVENTREE_VERSION} --depth 1 ${INVENTREE_REPO} ${INVENTREE_SRC_DIR} +RUN git clone --branch ${INVENTREE_BRANCH} --depth 1 ${INVENTREE_REPO} ${INVENTREE_SRC_DIR} # Setup Python virtual environment RUN python3 -m venv ${INVENTREE_VENV} From 71cac6e2697930904f834f33de06abd23015cd7b Mon Sep 17 00:00:00 2001 From: Oliver Walters <oliver.henry.walters@gmail.com> Date: Thu, 8 Apr 2021 00:09:51 +1000 Subject: [PATCH 071/111] Simplify waiting for db --- InvenTree/InvenTree/management/commands/wait_for_db.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/InvenTree/InvenTree/management/commands/wait_for_db.py b/InvenTree/InvenTree/management/commands/wait_for_db.py index 198c5dcf73..8709f5315e 100644 --- a/InvenTree/InvenTree/management/commands/wait_for_db.py +++ b/InvenTree/InvenTree/management/commands/wait_for_db.py @@ -7,7 +7,7 @@ from django.core.management.base import BaseCommand from django.db import connections from django.db.utils import OperationalError -import psycopg2 +from part.models import Part import time @@ -26,8 +26,12 @@ class Command(BaseCommand): try: # get the database with keyword 'default' from settings.py db_conn = connections['default'] + + # Try to read some data from the database + Part.objects.count() + # prints success messge in green self.stdout.write(self.style.SUCCESS('InvenTree database connected')) - except (OperationalError, psycopg2.OperationalError): + except: self.stdout.write(self.style.ERROR("Database unavailable, waiting 5 seconds ...")) time.sleep(5) From 3926276fd189410db823ee0d97eaf21c25f9d8e9 Mon Sep 17 00:00:00 2001 From: Oliver Walters <oliver.henry.walters@gmail.com> Date: Thu, 8 Apr 2021 00:37:34 +1000 Subject: [PATCH 072/111] Greatly simplified "wait_for_db" command --- .../management/commands/wait_for_db.py | 35 +++++++++++-------- 1 file changed, 20 insertions(+), 15 deletions(-) diff --git a/InvenTree/InvenTree/management/commands/wait_for_db.py b/InvenTree/InvenTree/management/commands/wait_for_db.py index 8709f5315e..dc510a5fd2 100644 --- a/InvenTree/InvenTree/management/commands/wait_for_db.py +++ b/InvenTree/InvenTree/management/commands/wait_for_db.py @@ -4,10 +4,8 @@ Custom management command, wait for the database to be ready! from django.core.management.base import BaseCommand -from django.db import connections -from django.db.utils import OperationalError - -from part.models import Part +from django.db import connection +from django.db.utils import OperationalError, ImproperlyConfigured import time @@ -18,20 +16,27 @@ class Command(BaseCommand): """ def handle(self, *args, **kwargs): + self.stdout.write("Waiting for database...") - db_conn = None + connected = False + + while not connected: + + time.sleep(5) - while not db_conn: try: - # get the database with keyword 'default' from settings.py - db_conn = connections['default'] + connection.ensure_connection() - # Try to read some data from the database - Part.objects.count() + connected = True - # prints success messge in green - self.stdout.write(self.style.SUCCESS('InvenTree database connected')) - except: - self.stdout.write(self.style.ERROR("Database unavailable, waiting 5 seconds ...")) - time.sleep(5) + except OperationalError as e: + self.stdout.write(f"Could not connect to database: {e}") + except ImproperlyConfigured as e: + self.stdout.write(f"Improperly configured: {e}") + else: + if not connection.is_usable(): + self.stdout.write("Database configuration is not usable") + + if connected: + self.stdout.write("Database connection sucessful!") \ No newline at end of file From 3381945e14910a98fc4cbf90f3e55ce82f3b3270 Mon Sep 17 00:00:00 2001 From: Oliver Walters <oliver.henry.walters@gmail.com> Date: Thu, 8 Apr 2021 17:10:48 +1000 Subject: [PATCH 073/111] Add newline --- InvenTree/InvenTree/management/commands/wait_for_db.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/InvenTree/InvenTree/management/commands/wait_for_db.py b/InvenTree/InvenTree/management/commands/wait_for_db.py index dc510a5fd2..b9fa4e5025 100644 --- a/InvenTree/InvenTree/management/commands/wait_for_db.py +++ b/InvenTree/InvenTree/management/commands/wait_for_db.py @@ -39,4 +39,4 @@ class Command(BaseCommand): self.stdout.write("Database configuration is not usable") if connected: - self.stdout.write("Database connection sucessful!") \ No newline at end of file + self.stdout.write("Database connection sucessful!") From 47a93bc4cbb63c7f33a8a4dd97d96539ce6f9959 Mon Sep 17 00:00:00 2001 From: Oliver Walters <oliver.henry.walters@gmail.com> Date: Thu, 8 Apr 2021 21:01:52 +1000 Subject: [PATCH 074/111] More environment variables for config.yaml --- InvenTree/InvenTree/settings.py | 7 +++++-- InvenTree/config_template.yaml | 3 +++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/InvenTree/InvenTree/settings.py b/InvenTree/InvenTree/settings.py index be9a4b729f..338a2dda9d 100644 --- a/InvenTree/InvenTree/settings.py +++ b/InvenTree/InvenTree/settings.py @@ -409,7 +409,7 @@ reqiured_keys = ['ENGINE', 'NAME'] for key in reqiured_keys: if key not in db_config: - error_msg = f'Missing required database configuration value {key} in config.yaml' + error_msg = f'Missing required database configuration value {key}' logger.error(error_msg) print('Error: ' + error_msg) @@ -503,7 +503,10 @@ LOCALE_PATHS = ( os.path.join(BASE_DIR, 'locale/'), ) -TIME_ZONE = CONFIG.get('timezone', 'UTC') +TIME_ZONE = get_setting( + 'INVENTREE_TIMEZONE', + CONFIG.get('timezone', 'UTC') +) USE_I18N = True diff --git a/InvenTree/config_template.yaml b/InvenTree/config_template.yaml index fb30f2d339..9e18adb759 100644 --- a/InvenTree/config_template.yaml +++ b/InvenTree/config_template.yaml @@ -49,6 +49,7 @@ language: en-us # System time-zone (default is UTC) # Reference: https://en.wikipedia.org/wiki/List_of_tz_database_time_zones # Select an option from the "TZ database name" column +# Use the environment variable INVENTREE_TIMEZONE timezone: UTC # List of currencies supported by default. @@ -63,6 +64,7 @@ currencies: - USD # Set debug to False to run in production mode +# Use the environment variable INVENTREE_DEBUG debug: True # Set debug_toolbar to True to enable a debugging toolbar for InvenTree @@ -71,6 +73,7 @@ debug: True debug_toolbar: False # Configure the system logging level +# Use environment variable INVENTREE_LOG_LEVEL # Options: DEBUG / INFO / WARNING / ERROR / CRITICAL log_level: WARNING From b5a5f5b4094b5eb59c4c63d50a6087110e300806 Mon Sep 17 00:00:00 2001 From: eeintech <eeintech@eeinte.ch> Date: Thu, 8 Apr 2021 13:42:35 -0400 Subject: [PATCH 075/111] Simplified stock table view in Part and SupplierPart detail pages --- .../company/templates/company/supplier_part_stock.html | 2 +- InvenTree/part/templates/part/stock.html | 2 +- InvenTree/templates/js/stock.js | 9 +++++++-- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/InvenTree/company/templates/company/supplier_part_stock.html b/InvenTree/company/templates/company/supplier_part_stock.html index 49a5a809c2..524c508957 100644 --- a/InvenTree/company/templates/company/supplier_part_stock.html +++ b/InvenTree/company/templates/company/supplier_part_stock.html @@ -22,7 +22,7 @@ params: { supplier_part: {{ part.id }}, location_detail: true, - part_detail: true, + part_detail: false, }, groupByField: 'location', buttons: ['#stock-options'], diff --git a/InvenTree/part/templates/part/stock.html b/InvenTree/part/templates/part/stock.html index cd4f9ab2fa..2d1385f89c 100644 --- a/InvenTree/part/templates/part/stock.html +++ b/InvenTree/part/templates/part/stock.html @@ -40,7 +40,7 @@ params: { part: {{ part.id }}, location_detail: true, - part_detail: true, + part_detail: false, }, groupByField: 'location', buttons: [ diff --git a/InvenTree/templates/js/stock.js b/InvenTree/templates/js/stock.js index 02714810e3..cba3e56b35 100644 --- a/InvenTree/templates/js/stock.js +++ b/InvenTree/templates/js/stock.js @@ -241,7 +241,7 @@ function loadStockTable(table, options) { // List of user-params which override the default filters - options.params['part_detail'] = true; + // options.params['part_detail'] = true; options.params['location_detail'] = true; var params = options.params || {}; @@ -524,7 +524,8 @@ function loadStockTable(table, options) { title: '{% trans "Part" %}', sortName: 'part__name', sortable: true, - switchable: false, + visible: params['part_detail'], + switchable: params['part_detail'], formatter: function(value, row, index, field) { var url = `/stock/item/${row.pk}/`; @@ -543,6 +544,8 @@ function loadStockTable(table, options) { title: 'IPN', sortName: 'part__IPN', sortable: true, + visible: params['part_detail'], + switchable: params['part_detail'], formatter: function(value, row, index, field) { return row.part_detail.IPN; }, @@ -550,6 +553,8 @@ function loadStockTable(table, options) { { field: 'part_detail.description', title: '{% trans "Description" %}', + visible: params['part_detail'], + switchable: params['part_detail'], formatter: function(value, row, index, field) { return row.part_detail.description; } From 7491cda313cd512f21cd7bb1988ad45a734bec9e Mon Sep 17 00:00:00 2001 From: eeintech <eeintech@eeinte.ch> Date: Thu, 8 Apr 2021 14:35:47 -0400 Subject: [PATCH 076/111] Replace normalize with integer wrapper for quantity field --- InvenTree/part/admin.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/InvenTree/part/admin.py b/InvenTree/part/admin.py index 4681404ba1..d08d5f1eaa 100644 --- a/InvenTree/part/admin.py +++ b/InvenTree/part/admin.py @@ -180,7 +180,7 @@ class BomItemResource(ModelResource): Ref: https://django-import-export.readthedocs.io/en/latest/getting_started.html#advanced-data-manipulation-on-export """ - return normalize(item.quantity) + return int(item.quantity) def before_export(self, queryset, *args, **kwargs): From cbddda6640693d8b2a035f8a1e6dadff452ecbe5 Mon Sep 17 00:00:00 2001 From: eeintech <eeintech@eeinte.ch> Date: Thu, 8 Apr 2021 14:41:06 -0400 Subject: [PATCH 077/111] Remove normalize import --- InvenTree/part/admin.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/InvenTree/part/admin.py b/InvenTree/part/admin.py index d08d5f1eaa..eb728c5295 100644 --- a/InvenTree/part/admin.py +++ b/InvenTree/part/admin.py @@ -16,8 +16,6 @@ from .models import PartCategoryParameterTemplate from .models import PartTestTemplate from .models import PartSellPriceBreak -from InvenTree.helpers import normalize - from stock.models import StockLocation from company.models import SupplierPart From 97e1bc0a67d18aa13e0b556607f067bffa1fc62b Mon Sep 17 00:00:00 2001 From: eeintech <eeintech@eeinte.ch> Date: Thu, 8 Apr 2021 21:46:11 -0400 Subject: [PATCH 078/111] Added missing part_detail reference --- InvenTree/templates/InvenTree/index.html | 1 + InvenTree/templates/js/stock.js | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/InvenTree/templates/InvenTree/index.html b/InvenTree/templates/InvenTree/index.html index b7807840c5..f7154e5fbb 100644 --- a/InvenTree/templates/InvenTree/index.html +++ b/InvenTree/templates/InvenTree/index.html @@ -131,6 +131,7 @@ addHeaderAction('stock-to-build', '{% trans "Required for Build Orders" %}', 'fa loadStockTable($('#table-recently-updated-stock'), { params: { + part_detail: true, ordering: "-updated", max_results: {% settings_value "STOCK_RECENT_COUNT" %}, }, diff --git a/InvenTree/templates/js/stock.js b/InvenTree/templates/js/stock.js index cba3e56b35..b163bc89f3 100644 --- a/InvenTree/templates/js/stock.js +++ b/InvenTree/templates/js/stock.js @@ -241,7 +241,6 @@ function loadStockTable(table, options) { // List of user-params which override the default filters - // options.params['part_detail'] = true; options.params['location_detail'] = true; var params = options.params || {}; From afddf12339c36b26f7dd3e7c200b9fe1b25f6ae9 Mon Sep 17 00:00:00 2001 From: eeintech <eeintech@eeinte.ch> Date: Thu, 8 Apr 2021 22:04:26 -0400 Subject: [PATCH 079/111] Changed int to float --- InvenTree/part/admin.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/InvenTree/part/admin.py b/InvenTree/part/admin.py index eb728c5295..af2f615803 100644 --- a/InvenTree/part/admin.py +++ b/InvenTree/part/admin.py @@ -178,7 +178,7 @@ class BomItemResource(ModelResource): Ref: https://django-import-export.readthedocs.io/en/latest/getting_started.html#advanced-data-manipulation-on-export """ - return int(item.quantity) + return float(item.quantity) def before_export(self, queryset, *args, **kwargs): From 6bf4140e5a4f311f4b289521c5c83bf6d99848db Mon Sep 17 00:00:00 2001 From: eeintech <eeintech@eeinte.ch> Date: Fri, 9 Apr 2021 16:55:05 -0400 Subject: [PATCH 080/111] Fixed transfer stock action in template --- InvenTree/stock/templates/stock/item_base.html | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/InvenTree/stock/templates/stock/item_base.html b/InvenTree/stock/templates/stock/item_base.html index a72b727f69..04518a0c65 100644 --- a/InvenTree/stock/templates/stock/item_base.html +++ b/InvenTree/stock/templates/stock/item_base.html @@ -165,13 +165,13 @@ InvenTree | {% trans "Stock Item" %} - {{ item }} {% if item.in_stock %} <li><a href='#' id='stock-remove' title='{% trans "Remove stock" %}'><span class='fas fa-minus-circle icon-red'></span> {% trans "Remove stock" %}</a></li> {% endif %} - {% if item.in_stock and item.can_adjust_location %} - <li><a href='#' id='stock-move' title='{% trans "Transfer stock" %}'><span class='fas fa-exchange-alt icon-blue'></span> {% trans "Transfer stock" %}</a></li> - {% endif %} {% if item.in_stock and item.part.trackable %} <li><a href='#' id='stock-serialize' title='{% trans "Serialize stock" %}'><span class='fas fa-hashtag'></span> {% trans "Serialize stock" %}</a> </li> {% endif %} {% endif %} + {% if item.in_stock and item.can_adjust_location %} + <li><a href='#' id='stock-move' title='{% trans "Transfer stock" %}'><span class='fas fa-exchange-alt icon-blue'></span> {% trans "Transfer stock" %}</a></li> + {% endif %} {% if item.in_stock and item.can_adjust_location and item.part.salable and not item.customer %} <li><a href='#' id='stock-assign-to-customer' title='{% trans "Assign to customer" %}'><span class='fas fa-user-tie'></span> {% trans "Assign to customer" %}</a></li> {% endif %} From 8eb571bddf292d697b5a92672af7087be4550081 Mon Sep 17 00:00:00 2001 From: Oliver Walters <oliver.henry.walters@gmail.com> Date: Sat, 10 Apr 2021 15:08:10 +1000 Subject: [PATCH 081/111] Update dockerfile --- InvenTree/InvenTree/settings.py | 2 +- docker/Dockerfile | 29 ++++++++++++---------------- docker/{start.sh => start_server.sh} | 4 ++-- docker/start_worker.sh | 24 +++++++++++++++++++++++ docker/supervisord.conf | 11 ++++++----- 5 files changed, 45 insertions(+), 25 deletions(-) rename docker/{start.sh => start_server.sh} (87%) create mode 100644 docker/start_worker.sh diff --git a/InvenTree/InvenTree/settings.py b/InvenTree/InvenTree/settings.py index 338a2dda9d..5e259a66e6 100644 --- a/InvenTree/InvenTree/settings.py +++ b/InvenTree/InvenTree/settings.py @@ -338,7 +338,7 @@ Q_CLUSTER = { 'queue_limit': 50, 'bulk': 10, 'orm': 'default', - 'sync': True, + 'sync': False, } # Markdownx configuration diff --git a/docker/Dockerfile b/docker/Dockerfile index a86fd09e45..893f028b9b 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -42,8 +42,6 @@ LABEL org.label-schema.schema-version="1.0" \ org.label-schema.vcs-branch=${BRANCH} \ org.label-schema.vcs-ref=${COMMIT} -RUN echo "Installing InvenTree '${INVENTREE_VERSION}' from ${INVENTREE_REPO}" - # Create user account RUN addgroup -S inventreegroup && adduser -S inventree -G inventreegroup WORKDIR ${INVENTREE_HOME} @@ -60,8 +58,8 @@ RUN apk add --no-cache python3 RUN apk add --no-cache postgresql-contrib postgresql-dev libpq RUN apk add --no-cache mariadb-connector-c mariadb-dev -# Clone source code -RUN git clone --branch ${INVENTREE_BRANCH} --depth 1 ${INVENTREE_REPO} ${INVENTREE_SRC_DIR} +# Create required directories +RUN mkdir ${INVENTREE_HOME}/media ${INVENTREE_HOME}/static ${INVENTREE_HOME}/backup # Setup Python virtual environment RUN python3 -m venv ${INVENTREE_VENV} @@ -72,25 +70,22 @@ RUN source ${INVENTREE_VENV}/bin/activate && pip install --no-cache-dir -U invok RUN source ${INVENTREE_VENV}/bin/activate && pip install --no-cache-dir -U psycopg2 mysqlclient pgcli mariadb RUN source ${INVENTREE_VENV}/bin/activate && pip install --no-cache-dir -U gunicorn +# Clone source code +RUN echo "Downloading InvenTree from ${INVENTREE_REPO}" +RUN git clone --branch ${INVENTREE_BRANCH} --depth 1 ${INVENTREE_REPO} ${INVENTREE_SRC_DIR} + # Install InvenTree packages RUN source ${INVENTREE_VENV}/bin/activate && pip install --no-cache-dir -U -r ${INVENTREE_SRC_DIR}/requirements.txt -# Install supervisor -RUN apk add --no-cache supervisor - -# Create required directories -RUN mkdir ${INVENTREE_HOME}/media ${INVENTREE_HOME}/static ${INVENTREE_HOME}/backup - -# Copy supervisor file -COPY supervisord.conf ${INVENTREE_HOME}/supervisord.conf - # Copy gunicorn config file COPY gunicorn.conf.py ${INVENTREE_HOME}/gunicorn.conf.py -# Copy startup script -COPY start.sh ${INVENTREE_HOME}/start.sh +# Copy startup scripts +COPY start_server.sh ${INVENTREE_HOME}/start_server.sh +COPY start_worker.sh ${INVENTREE_HOME}/start_worker.sh -RUN chmod 755 ${INVENTREE_HOME}/start.sh +RUN chmod 755 ${INVENTREE_HOME}/start_server.sh +RUN chmod 755 ${INVENTREE_HOME}/start_worker.sh # Let us begin -CMD ["bash", "./start.sh"] +CMD ["bash", "./start_server.sh"] diff --git a/docker/start.sh b/docker/start_server.sh similarity index 87% rename from docker/start.sh rename to docker/start_server.sh index f17150d0ca..fb84783829 100644 --- a/docker/start.sh +++ b/docker/start_server.sh @@ -29,5 +29,5 @@ python manage.py migrate --run-syncdb || exit 1 python manage.py collectstatic --noinput || exit 1 python manage.py clearsessions || exit 1 -# Now we can launch the server and background worker -/usr/bin/supervisord -c $INVENTREE_HOME/supervisord.conf \ No newline at end of file +# Now we can launch the server +gunicorn -c $INVENTREE_HOME/gunicorn.conf.py InvenTree.wsgi -b 0.0.0.0:8080 diff --git a/docker/start_worker.sh b/docker/start_worker.sh new file mode 100644 index 0000000000..9b7f3f408e --- /dev/null +++ b/docker/start_worker.sh @@ -0,0 +1,24 @@ +#!/bin/sh + +echo "Starting InvenTree worker..." + +# Check that the database engine is specified +if [ -z "$INVENTREE_DB_ENGINE" ]; then + echo "INVENTREE_DB_ENGINE not configured" + exit 1 +fi + +# Activate virtual environment +source ./env/bin/activate + +sleep 5 + +# Wait for the database to be ready +cd src/InvenTree + +python manage.py wait_for_db + +sleep 10 + +# Now we can launch the background worker process +python manage.py qcluster diff --git a/docker/supervisord.conf b/docker/supervisord.conf index 8dc8e77fc1..b99c48edcf 100644 --- a/docker/supervisord.conf +++ b/docker/supervisord.conf @@ -16,18 +16,20 @@ user=inventree nodaemon=true +[supervisorctl] +serverurl=unix:///var/run/supervisor.sock + [program:inventree-server] user=inventree directory=%(ENV_INVENTREE_SRC_DIR)s/InvenTree -command=%(ENV_INVENTREE_VENV)s/bin/gunicorn -c %(ENV_INVENTREE_HOME)s/gunicorn.conf.py InvenTree.wsgi -b 0.0.0.0:8080 +command=%(ENV_INVENTREE_VENV)s/bin/gunicorn -c %(ENV_INVENTREE_HOME)s/gunicorn.conf.py InvenTree.wsgi -b 127.0.0.1:8080 startsecs=10 autostart=true autorestart=true startretries=3 stdout_logfile=/dev/fd/1 stdout_logfile_maxbytes=0 -stderr_logfile=/dev/fd/2 -stderr_logfile_maxbytes=0 +redirect_stderr=true [program:inventree-cluster] user=inventree @@ -38,5 +40,4 @@ autostart=true autorestart=true stdout_logfile=/dev/fd/1 stdout_logfile_maxbytes=0 -stderr_logfile=/dev/fd/2 -stderr_logfile_maxbytes=0 \ No newline at end of file +redirect_stderr=true \ No newline at end of file From 1372343bd5fd6580a4aba5055fa9a7795308ace9 Mon Sep 17 00:00:00 2001 From: Oliver Walters <oliver.henry.walters@gmail.com> Date: Sat, 10 Apr 2021 15:27:50 +1000 Subject: [PATCH 082/111] Updates to docker-compose file - Note: not ready yet! --- docker/docker-compose.yml | 41 ++++++++++++++++++++++++++++++++++----- 1 file changed, 36 insertions(+), 5 deletions(-) diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index abf57c24a7..ba9d8e1261 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -3,6 +3,8 @@ version: "3.8" # Docker compose recipe for InvenTree # - Runs PostgreSQL as the database backend # - Serves web data using Gunicorn +# - Runs the background worker process +# - Runs nginx as a reverse proxy services: # Use PostgreSQL as the database backend @@ -10,7 +12,7 @@ services: # just make sure that you change the INVENTREE_DB_xxx vars below db: image: postgres - container_name: db + container_name: inventree_db ports: - 5432/tcp environment: @@ -21,12 +23,16 @@ services: - database_data:/var/lib/postgresql/data/ restart: unless-stopped - inventree: - build: . + server: + build: + context: . + args: + repository: "https://github.com/SchrodingersGat/InvenTree.git" + branch: "django-q" image: inventree/inventree:latest - container_name: inventree + container_name: inventree_server ports: - - 8080:8080 + - "8080:8080" depends_on: - db volumes: @@ -42,6 +48,31 @@ services: - INVENTREE_DB_HOST=db restart: unless-stopped + worker: + build: + context: . + args: + repository: "https://github.com/SchrodingersGat/InvenTree.git" + branch: "django-q" + entrypoint: ./start_worker.sh + image: inventree/worker:latest + container_name: inventree_worker + depends_on: + - db + - server + volumes: + - static_volume:/home/inventree/static + - media_volume:/home/inventree/media + - backup_volume:/home/inventree/backup + environment: + - INVENTREE_DB_ENGINE=postgresql + - INVENTREE_DB_NAME=inventree + - INVENTREE_DB_USER=pguser + - INVENTREE_DB_PASSWORD=pgpassword + - INVENTREE_DB_PORT=5432 + - INVENTREE_DB_HOST=db + restart: unless-stopped + volumes: database_data: static_volume: From e6bd91c9e2fc72b380dd5212990a3796df5b467b Mon Sep 17 00:00:00 2001 From: Oliver Walters <oliver.henry.walters@gmail.com> Date: Sat, 10 Apr 2021 15:29:44 +1000 Subject: [PATCH 083/111] Company description is no longer a required field --- .../migrations/0033_auto_20210410_1528.py | 18 ++++++++++++++++++ InvenTree/company/models.py | 7 ++++++- 2 files changed, 24 insertions(+), 1 deletion(-) create mode 100644 InvenTree/company/migrations/0033_auto_20210410_1528.py diff --git a/InvenTree/company/migrations/0033_auto_20210410_1528.py b/InvenTree/company/migrations/0033_auto_20210410_1528.py new file mode 100644 index 0000000000..12153a9ef6 --- /dev/null +++ b/InvenTree/company/migrations/0033_auto_20210410_1528.py @@ -0,0 +1,18 @@ +# Generated by Django 3.0.7 on 2021-04-10 05:28 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('company', '0032_auto_20210403_1837'), + ] + + operations = [ + migrations.AlterField( + model_name='company', + name='description', + field=models.CharField(blank=True, help_text='Description of the company', max_length=500, verbose_name='Company description'), + ), + ] diff --git a/InvenTree/company/models.py b/InvenTree/company/models.py index 17091dcbc3..d83748c930 100644 --- a/InvenTree/company/models.py +++ b/InvenTree/company/models.py @@ -95,7 +95,12 @@ class Company(models.Model): help_text=_('Company name'), verbose_name=_('Company name')) - description = models.CharField(max_length=500, verbose_name=_('Company description'), help_text=_('Description of the company')) + description = models.CharField( + max_length=500, + verbose_name=_('Company description'), + help_text=_('Description of the company'), + blank=True, + ) website = models.URLField(blank=True, verbose_name=_('Website'), help_text=_('Company website URL')) From 9086c8a3bf202be3debbd81f86aa5681bd7bbe29 Mon Sep 17 00:00:00 2001 From: Oliver Walters <oliver.henry.walters@gmail.com> Date: Sat, 10 Apr 2021 17:36:19 +1000 Subject: [PATCH 084/111] Simplify external directory structure - All InvenTree data now in a single subdir - Copy default config file (if it does not exist) - Config file is accessible from outside world - Update start_server and start_worker scripts --- docker/Dockerfile | 12 +++++++----- docker/docker-compose.yml | 20 ++++++-------------- docker/start_server.sh | 28 ++++++++++++++++++++++------ docker/start_worker.sh | 6 ------ 4 files changed, 35 insertions(+), 31 deletions(-) diff --git a/docker/Dockerfile b/docker/Dockerfile index 893f028b9b..89af08f835 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -18,12 +18,14 @@ ENV INVENTREE_LOG_LEVEL="INFO" # InvenTree paths ENV INVENTREE_SRC_DIR="${INVENTREE_HOME}/src" ENV INVENTREE_MNG_DIR="${INVENTREE_SRC_DIR}/InvenTree" -ENV INVENTREE_STATIC_ROOT="${INVENTREE_HOME}/static" -ENV INVENTREE_MEDIA_ROOT="${INVENTREE_HOME}/media" -ENV INVENTREE_LOG_DIR="${INVENTREE_HOME}/log" -ENV INVENTREE_BACKUP_DIR="${INVENTREE_HOME}/backup" +ENV INVENTREE_DATA_DIR="${INVENTREE_HOME}/data" +ENV INVENTREE_STATIC_ROOT="${INVENTREE_DATA_DIR}/static" +ENV INVENTREE_MEDIA_ROOT="${INVENTREE_MEDIA_DIR}/media" +ENV INVENTREE_BACKUP_DIR="${INVENTREE_DATA_DIR}/backup" ENV INVENTREE_VENV="${INVENTREE_HOME}/env" +ENV INVENTREE_CONFIG_FILE="${INVENTREE_DATA_DIR}/config.yaml" + # Pass DB configuration through as environment variables ENV INVENTREE_DB_ENGINE="${INVENTREE_DB_ENGINE}" ENV INVENTREE_DB_NAME="${INVENTREE_DB_NAME}" @@ -59,7 +61,7 @@ RUN apk add --no-cache postgresql-contrib postgresql-dev libpq RUN apk add --no-cache mariadb-connector-c mariadb-dev # Create required directories -RUN mkdir ${INVENTREE_HOME}/media ${INVENTREE_HOME}/static ${INVENTREE_HOME}/backup +#RUN mkdir ${INVENTREE_DATA_DIR}}/media ${INVENTREE_HOME}/static ${INVENTREE_HOME}/backup # Setup Python virtual environment RUN python3 -m venv ${INVENTREE_VENV} diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index ba9d8e1261..d7d2c09cdb 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -20,7 +20,9 @@ services: - POSTGRES_USER=pguser - POSTGRES_PASSWORD=pgpassword volumes: - - database_data:/var/lib/postgresql/data/ + # Map external directory to store database data + # Replace /path/to/dir with the required external path + - /mnt/c/abcdatabase:/var/lib/postgresql/data/ restart: unless-stopped server: @@ -36,9 +38,9 @@ services: depends_on: - db volumes: - - static_volume:/home/inventree/static - - media_volume:/home/inventree/media - - backup_volume:/home/inventree/backup + # Map external directory to store InvenTree data + # Replace /path/to/dir with the required external path + - /mnt/c/abcde:/home/inventree/data environment: - INVENTREE_DB_ENGINE=postgresql - INVENTREE_DB_NAME=inventree @@ -60,10 +62,6 @@ services: depends_on: - db - server - volumes: - - static_volume:/home/inventree/static - - media_volume:/home/inventree/media - - backup_volume:/home/inventree/backup environment: - INVENTREE_DB_ENGINE=postgresql - INVENTREE_DB_NAME=inventree @@ -72,9 +70,3 @@ services: - INVENTREE_DB_PORT=5432 - INVENTREE_DB_HOST=db restart: unless-stopped - -volumes: - database_data: - static_volume: - media_volume: - backup_volume: diff --git a/docker/start_server.sh b/docker/start_server.sh index fb84783829..e9eaff1dfa 100644 --- a/docker/start_server.sh +++ b/docker/start_server.sh @@ -1,17 +1,33 @@ #!/bin/sh -echo "Starting InvenTree server..." +# Create required directory structure (if it does not already exist) +if [[ ! -d "$INVENTREE_STATIC_ROOT" ]]; then + echo "Creating directory $INVENTREE_STATIC_ROOT" + mkdir $INVENTREE_STATIC_ROOT +fi -# Check that the database engine is specified -if [ -z "$INVENTREE_DB_ENGINE" ]; then - echo "INVENTREE_DB_ENGINE not configured" - exit 1 +if [[ ! -d "$INVENTREE_MEDIA_ROOT" ]]; then + echo "Creating directory $INVENTREE_MEDIA_ROOT" + mkdir $INVENTREE_MEDIA_ROOT +fi + +if [[ ! -d "$INVENTREE_BACKUP_DIR" ]]; then + echo "Creating directory $INVENTREE_BACKUP_DIR" + mkdir $INVENTREE_BACKUP_DIR +fi + +# Check if "config.yaml" has been copied into the correct location +if test -f "$INVENTREE_CONFIG_FILE"; then + echo "$INVENTREE_CONFIG_FILE exists - skipping" +else + echo "Copying config file to $INVENTREE_CONFIG_FILE" + cp $INVENTREE_SRC_DIR/InvenTree/config_template.yaml $INVENTREE_CONFIG_FILE fi # Activate virtual environment source $INVENTREE_VENV/bin/activate -sleep 5 +echo "Starting InvenTree server..." # Wait for the database to be ready cd $INVENTREE_MNG_DIR diff --git a/docker/start_worker.sh b/docker/start_worker.sh index 9b7f3f408e..a8a4815583 100644 --- a/docker/start_worker.sh +++ b/docker/start_worker.sh @@ -2,12 +2,6 @@ echo "Starting InvenTree worker..." -# Check that the database engine is specified -if [ -z "$INVENTREE_DB_ENGINE" ]; then - echo "INVENTREE_DB_ENGINE not configured" - exit 1 -fi - # Activate virtual environment source ./env/bin/activate From 5e54b0f5cfc9b628451c23d30faaf89abb051eed Mon Sep 17 00:00:00 2001 From: Oliver Walters <oliver.henry.walters@gmail.com> Date: Sat, 10 Apr 2021 19:01:02 +1000 Subject: [PATCH 085/111] Auto-generate key file if it does not exist! --- InvenTree/InvenTree/settings.py | 24 +++++++++++------------- docker/Dockerfile | 3 ++- 2 files changed, 13 insertions(+), 14 deletions(-) diff --git a/InvenTree/InvenTree/settings.py b/InvenTree/InvenTree/settings.py index 5e259a66e6..fe0164af94 100644 --- a/InvenTree/InvenTree/settings.py +++ b/InvenTree/InvenTree/settings.py @@ -138,24 +138,22 @@ else: key_file = os.getenv("INVENTREE_SECRET_KEY_FILE") if key_file: - if os.path.isfile(key_file): - logger.info("SECRET_KEY loaded by INVENTREE_SECRET_KEY_FILE") - else: - logger.error(f"Secret key file '{key_file}'' not found") - exit(-1) + key_file = os.path.abspath(key_file) else: # default secret key location key_file = os.path.join(BASE_DIR, "secret_key.txt") + key_file = os.path.abspath(key_file) - if not os.path.exists(key_file): - logger.info("Creating key file 'secret_key.txt'") - # Create a random key file - with open(key_file, 'w') as f: - options = string.digits + string.ascii_letters + string.punctuation - key = ''.join([random.choice(options) for i in range(50)]) - f.write(key) + if not os.path.exists(key_file): + logger.info(f"Generating random key file at '{key_file}'") + # Create a random key file + with open(key_file, 'w') as f: + options = string.digits + string.ascii_letters + string.punctuation + key = ''.join([random.choice(options) for i in range(100)]) + f.write(key) - logger.info(f"SECRET_KEY loaded from {key_file}") + logger.info(f"Loading SECRET_KEY from '{key_file}'") + try: SECRET_KEY = open(key_file, "r").read().strip() except Exception: diff --git a/docker/Dockerfile b/docker/Dockerfile index 89af08f835..1fd9329d69 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -20,11 +20,12 @@ ENV INVENTREE_SRC_DIR="${INVENTREE_HOME}/src" ENV INVENTREE_MNG_DIR="${INVENTREE_SRC_DIR}/InvenTree" ENV INVENTREE_DATA_DIR="${INVENTREE_HOME}/data" ENV INVENTREE_STATIC_ROOT="${INVENTREE_DATA_DIR}/static" -ENV INVENTREE_MEDIA_ROOT="${INVENTREE_MEDIA_DIR}/media" +ENV INVENTREE_MEDIA_ROOT="${INVENTREE_DATA_DIR}/media" ENV INVENTREE_BACKUP_DIR="${INVENTREE_DATA_DIR}/backup" ENV INVENTREE_VENV="${INVENTREE_HOME}/env" ENV INVENTREE_CONFIG_FILE="${INVENTREE_DATA_DIR}/config.yaml" +ENV INVENTREE_SECRET_KEY_FILE="${INVENTREE_DATA_DIR}/secret_key.txt" # Pass DB configuration through as environment variables ENV INVENTREE_DB_ENGINE="${INVENTREE_DB_ENGINE}" From e787c853e5b53c18f1d145b5366b82aea3e6b174 Mon Sep 17 00:00:00 2001 From: Oliver Walters <oliver.henry.walters@gmail.com> Date: Sat, 10 Apr 2021 20:08:13 +1000 Subject: [PATCH 086/111] Update logger context --- InvenTree/InvenTree/api.py | 2 +- InvenTree/InvenTree/apps.py | 2 +- InvenTree/InvenTree/middleware.py | 2 +- InvenTree/InvenTree/settings.py | 4 ++-- InvenTree/InvenTree/status.py | 2 +- InvenTree/InvenTree/tasks.py | 2 +- InvenTree/barcodes/barcode.py | 2 +- InvenTree/company/apps.py | 2 +- InvenTree/label/apps.py | 2 +- InvenTree/label/models.py | 2 +- InvenTree/order/views.py | 2 +- InvenTree/part/apps.py | 2 +- InvenTree/part/models.py | 2 +- InvenTree/plugins/action/action.py | 2 +- InvenTree/plugins/plugins.py | 2 +- InvenTree/report/apps.py | 2 +- InvenTree/report/models.py | 2 +- InvenTree/users/models.py | 2 +- 18 files changed, 19 insertions(+), 19 deletions(-) diff --git a/InvenTree/InvenTree/api.py b/InvenTree/InvenTree/api.py index 2d04195d42..c7386e8fbc 100644 --- a/InvenTree/InvenTree/api.py +++ b/InvenTree/InvenTree/api.py @@ -23,7 +23,7 @@ from .version import inventreeVersion, inventreeApiVersion, inventreeInstanceNam from plugins import plugins as inventree_plugins -logger = logging.getLogger(__name__) +logger = logging.getLogger("inventree") logger.info("Loading action plugins...") diff --git a/InvenTree/InvenTree/apps.py b/InvenTree/InvenTree/apps.py index 07f565e012..03eb2bcb60 100644 --- a/InvenTree/InvenTree/apps.py +++ b/InvenTree/InvenTree/apps.py @@ -8,7 +8,7 @@ from django.core.exceptions import AppRegistryNotReady import InvenTree.tasks -logger = logging.getLogger(__name__) +logger = logging.getLogger("inventree") class InvenTreeConfig(AppConfig): diff --git a/InvenTree/InvenTree/middleware.py b/InvenTree/InvenTree/middleware.py index 2f1cf3a157..a34df4b7bd 100644 --- a/InvenTree/InvenTree/middleware.py +++ b/InvenTree/InvenTree/middleware.py @@ -8,7 +8,7 @@ import operator from rest_framework.authtoken.models import Token -logger = logging.getLogger(__name__) +logger = logging.getLogger("inventree") class AuthRequiredMiddleware(object): diff --git a/InvenTree/InvenTree/settings.py b/InvenTree/InvenTree/settings.py index fe0164af94..a8a2060451 100644 --- a/InvenTree/InvenTree/settings.py +++ b/InvenTree/InvenTree/settings.py @@ -116,7 +116,7 @@ LOGGING = { } # Get a logger instance for this setup file -logger = logging.getLogger(__name__) +logger = logging.getLogger("inventree") """ Specify a secret key to be used by django. @@ -153,7 +153,7 @@ else: f.write(key) logger.info(f"Loading SECRET_KEY from '{key_file}'") - + try: SECRET_KEY = open(key_file, "r").read().strip() except Exception: diff --git a/InvenTree/InvenTree/status.py b/InvenTree/InvenTree/status.py index 92e4737f25..42160927b0 100644 --- a/InvenTree/InvenTree/status.py +++ b/InvenTree/InvenTree/status.py @@ -11,7 +11,7 @@ from datetime import datetime, timedelta from django_q.models import Success from django_q.monitor import Stat -logger = logging.getLogger(__name__) +logger = logging.getLogger("inventree") def is_q_cluster_running(**kwargs): diff --git a/InvenTree/InvenTree/tasks.py b/InvenTree/InvenTree/tasks.py index c12d0ec4df..4829514f19 100644 --- a/InvenTree/InvenTree/tasks.py +++ b/InvenTree/InvenTree/tasks.py @@ -12,7 +12,7 @@ from django.core.exceptions import AppRegistryNotReady from django.db.utils import OperationalError, ProgrammingError -logger = logging.getLogger(__name__) +logger = logging.getLogger("inventree") def schedule_task(taskname, **kwargs): diff --git a/InvenTree/barcodes/barcode.py b/InvenTree/barcodes/barcode.py index 412065bf75..a00e91d7e4 100644 --- a/InvenTree/barcodes/barcode.py +++ b/InvenTree/barcodes/barcode.py @@ -12,7 +12,7 @@ from stock.serializers import StockItemSerializer, LocationSerializer from part.serializers import PartSerializer -logger = logging.getLogger(__name__) +logger = logging.getLogger('inventree') def hash_barcode(barcode_data): diff --git a/InvenTree/company/apps.py b/InvenTree/company/apps.py index 3fa3197183..53e884c50f 100644 --- a/InvenTree/company/apps.py +++ b/InvenTree/company/apps.py @@ -9,7 +9,7 @@ from django.conf import settings from PIL import UnidentifiedImageError -logger = logging.getLogger(__name__) +logger = logging.getLogger("inventree") class CompanyConfig(AppConfig): diff --git a/InvenTree/label/apps.py b/InvenTree/label/apps.py index 9f2d3ea9c4..4200b6e8bc 100644 --- a/InvenTree/label/apps.py +++ b/InvenTree/label/apps.py @@ -7,7 +7,7 @@ from django.apps import AppConfig from django.conf import settings -logger = logging.getLogger(__name__) +logger = logging.getLogger("inventree") def hashFile(filename): diff --git a/InvenTree/label/models.py b/InvenTree/label/models.py index c76b3f80c8..96850f4cb0 100644 --- a/InvenTree/label/models.py +++ b/InvenTree/label/models.py @@ -32,7 +32,7 @@ except OSError as err: sys.exit(1) -logger = logging.getLogger(__name__) +logger = logging.getLogger("inventree") def rename_label(instance, filename): diff --git a/InvenTree/order/views.py b/InvenTree/order/views.py index 7dc8b4efff..284a24fcf5 100644 --- a/InvenTree/order/views.py +++ b/InvenTree/order/views.py @@ -37,7 +37,7 @@ from InvenTree.views import InvenTreeRoleMixin from InvenTree.status_codes import PurchaseOrderStatus, SalesOrderStatus, StockStatus -logger = logging.getLogger(__name__) +logger = logging.getLogger("inventree") class PurchaseOrderIndex(InvenTreeRoleMixin, ListView): diff --git a/InvenTree/part/apps.py b/InvenTree/part/apps.py index d08e7680fe..11329abdd9 100644 --- a/InvenTree/part/apps.py +++ b/InvenTree/part/apps.py @@ -9,7 +9,7 @@ from django.conf import settings from PIL import UnidentifiedImageError -logger = logging.getLogger(__name__) +logger = logging.getLogger("inventree") class PartConfig(AppConfig): diff --git a/InvenTree/part/models.py b/InvenTree/part/models.py index e10dfba4ba..bd02672b3e 100644 --- a/InvenTree/part/models.py +++ b/InvenTree/part/models.py @@ -52,7 +52,7 @@ import common.models import part.settings as part_settings -logger = logging.getLogger(__name__) +logger = logging.getLogger("inventree") class PartCategory(InvenTreeTree): diff --git a/InvenTree/plugins/action/action.py b/InvenTree/plugins/action/action.py index 8d70302021..d61838f49b 100644 --- a/InvenTree/plugins/action/action.py +++ b/InvenTree/plugins/action/action.py @@ -5,7 +5,7 @@ import logging import plugins.plugin as plugin -logger = logging.getLogger(__name__) +logger = logging.getLogger("inventree") class ActionPlugin(plugin.InvenTreePlugin): diff --git a/InvenTree/plugins/plugins.py b/InvenTree/plugins/plugins.py index abb167d173..f6b68112bc 100644 --- a/InvenTree/plugins/plugins.py +++ b/InvenTree/plugins/plugins.py @@ -10,7 +10,7 @@ import plugins.action as action from plugins.action.action import ActionPlugin -logger = logging.getLogger(__name__) +logger = logging.getLogger("inventree") def iter_namespace(pkg): diff --git a/InvenTree/report/apps.py b/InvenTree/report/apps.py index 941133e481..77529263f6 100644 --- a/InvenTree/report/apps.py +++ b/InvenTree/report/apps.py @@ -6,7 +6,7 @@ from django.apps import AppConfig from django.conf import settings -logger = logging.getLogger(__name__) +logger = logging.getLogger("inventree") class ReportConfig(AppConfig): diff --git a/InvenTree/report/models.py b/InvenTree/report/models.py index 6f3891d6be..4e218a2c50 100644 --- a/InvenTree/report/models.py +++ b/InvenTree/report/models.py @@ -38,7 +38,7 @@ except OSError as err: sys.exit(1) -logger = logging.getLogger(__name__) +logger = logging.getLogger("inventree") class ReportFileUpload(FileSystemStorage): diff --git a/InvenTree/users/models.py b/InvenTree/users/models.py index 07bf2296ba..e454281b37 100644 --- a/InvenTree/users/models.py +++ b/InvenTree/users/models.py @@ -15,7 +15,7 @@ from django.db.models.signals import post_save, post_delete import logging -logger = logging.getLogger(__name__) +logger = logging.getLogger("inventree") class RuleSet(models.Model): From 178715ce6113b96aee9ec590498736fceee8485e Mon Sep 17 00:00:00 2001 From: Oliver Walters <oliver.henry.walters@gmail.com> Date: Sat, 10 Apr 2021 20:57:56 +1000 Subject: [PATCH 087/111] Auto create config file in specified location if it does not exist --- InvenTree/InvenTree/settings.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/InvenTree/InvenTree/settings.py b/InvenTree/InvenTree/settings.py index a8a2060451..9a6a4d59ae 100644 --- a/InvenTree/InvenTree/settings.py +++ b/InvenTree/InvenTree/settings.py @@ -64,9 +64,6 @@ if cfg_filename: cfg_filename = cfg_filename.strip() cfg_filename = os.path.abspath(cfg_filename) - if not os.path.exists(cfg_filename): - print(f"InvenTree configuration file '{cfg_filename}' does not exist!") - sys.exit(1) else: # Config file is *not* specified - use the default cfg_filename = os.path.join(BASE_DIR, 'config.yaml') @@ -76,6 +73,7 @@ if not os.path.exists(cfg_filename): cfg_template = os.path.join(BASE_DIR, "config_template.yaml") shutil.copyfile(cfg_template, cfg_filename) + print(f"Created config file {cfg_filename}") with open(cfg_filename, 'r') as cfg: CONFIG = yaml.safe_load(cfg) From 823f84e46a75b178a652b046b56f00b82c80fbe1 Mon Sep 17 00:00:00 2001 From: Oliver Walters <oliver.henry.walters@gmail.com> Date: Sat, 10 Apr 2021 20:58:51 +1000 Subject: [PATCH 088/111] Simplified volume management in docker-compose --- docker/docker-compose.yml | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index d7d2c09cdb..08eb84d45b 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -20,9 +20,7 @@ services: - POSTGRES_USER=pguser - POSTGRES_PASSWORD=pgpassword volumes: - # Map external directory to store database data - # Replace /path/to/dir with the required external path - - /mnt/c/abcdatabase:/var/lib/postgresql/data/ + - data:/var/lib/postgresql/data/ restart: unless-stopped server: @@ -38,9 +36,7 @@ services: depends_on: - db volumes: - # Map external directory to store InvenTree data - # Replace /path/to/dir with the required external path - - /mnt/c/abcde:/home/inventree/data + - data:/home/inventree/data environment: - INVENTREE_DB_ENGINE=postgresql - INVENTREE_DB_NAME=inventree @@ -62,6 +58,8 @@ services: depends_on: - db - server + volumes: + - data:/home/inventree/data environment: - INVENTREE_DB_ENGINE=postgresql - INVENTREE_DB_NAME=inventree @@ -70,3 +68,12 @@ services: - INVENTREE_DB_PORT=5432 - INVENTREE_DB_HOST=db restart: unless-stopped + +volumes: + data: + driver: local + driver_opts: + type: none + o: bind + # This directory specified where InvenTree data are stored "outside" the docker containers + device: c:/abcdef \ No newline at end of file From 2f1db486a038e19002ce3e5ed7dc650598226150 Mon Sep 17 00:00:00 2001 From: Oliver Walters <oliver.henry.walters@gmail.com> Date: Sat, 10 Apr 2021 21:40:27 +1000 Subject: [PATCH 089/111] Do not use python virtual environment inside container --- docker/Dockerfile | 16 ++++++---------- docker/docker-compose.yml | 7 +++++-- docker/start_server.sh | 3 --- docker/start_worker.sh | 3 --- 4 files changed, 11 insertions(+), 18 deletions(-) diff --git a/docker/Dockerfile b/docker/Dockerfile index 1fd9329d69..eefc700cc7 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -22,7 +22,6 @@ ENV INVENTREE_DATA_DIR="${INVENTREE_HOME}/data" ENV INVENTREE_STATIC_ROOT="${INVENTREE_DATA_DIR}/static" ENV INVENTREE_MEDIA_ROOT="${INVENTREE_DATA_DIR}/media" ENV INVENTREE_BACKUP_DIR="${INVENTREE_DATA_DIR}/backup" -ENV INVENTREE_VENV="${INVENTREE_HOME}/env" ENV INVENTREE_CONFIG_FILE="${INVENTREE_DATA_DIR}/config.yaml" ENV INVENTREE_SECRET_KEY_FILE="${INVENTREE_DATA_DIR}/secret_key.txt" @@ -64,21 +63,18 @@ RUN apk add --no-cache mariadb-connector-c mariadb-dev # Create required directories #RUN mkdir ${INVENTREE_DATA_DIR}}/media ${INVENTREE_HOME}/static ${INVENTREE_HOME}/backup -# Setup Python virtual environment -RUN python3 -m venv ${INVENTREE_VENV} - -# Install required PIP packages (into the virtual environment!) -RUN source ${INVENTREE_VENV}/bin/activate && pip install --upgrade pip setuptools wheel -RUN source ${INVENTREE_VENV}/bin/activate && pip install --no-cache-dir -U invoke -RUN source ${INVENTREE_VENV}/bin/activate && pip install --no-cache-dir -U psycopg2 mysqlclient pgcli mariadb -RUN source ${INVENTREE_VENV}/bin/activate && pip install --no-cache-dir -U gunicorn +# Install required python packages +RUN pip install --upgrade pip setuptools wheel +RUN pip install --no-cache-dir -U invoke +RUN pip install --no-cache-dir -U psycopg2 mysqlclient pgcli mariadb +RUN pip install --no-cache-dir -U gunicorn # Clone source code RUN echo "Downloading InvenTree from ${INVENTREE_REPO}" RUN git clone --branch ${INVENTREE_BRANCH} --depth 1 ${INVENTREE_REPO} ${INVENTREE_SRC_DIR} # Install InvenTree packages -RUN source ${INVENTREE_VENV}/bin/activate && pip install --no-cache-dir -U -r ${INVENTREE_SRC_DIR}/requirements.txt +RUN pip install --no-cache-dir -U -r ${INVENTREE_SRC_DIR}/requirements.txt # Copy gunicorn config file COPY gunicorn.conf.py ${INVENTREE_HOME}/gunicorn.conf.py diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index 08eb84d45b..839128738d 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -7,6 +7,7 @@ version: "3.8" # - Runs nginx as a reverse proxy services: + # Database service # Use PostgreSQL as the database backend # Note: this can be changed to a different backend, # just make sure that you change the INVENTREE_DB_xxx vars below @@ -23,7 +24,9 @@ services: - data:/var/lib/postgresql/data/ restart: unless-stopped - server: + # InvenTree web server services + # Uses gunicorn as the web server + inventree: build: context: . args: @@ -57,7 +60,7 @@ services: container_name: inventree_worker depends_on: - db - - server + - inventree volumes: - data:/home/inventree/data environment: diff --git a/docker/start_server.sh b/docker/start_server.sh index e9eaff1dfa..0436cd532f 100644 --- a/docker/start_server.sh +++ b/docker/start_server.sh @@ -24,9 +24,6 @@ else cp $INVENTREE_SRC_DIR/InvenTree/config_template.yaml $INVENTREE_CONFIG_FILE fi -# Activate virtual environment -source $INVENTREE_VENV/bin/activate - echo "Starting InvenTree server..." # Wait for the database to be ready diff --git a/docker/start_worker.sh b/docker/start_worker.sh index a8a4815583..64c8d75ee4 100644 --- a/docker/start_worker.sh +++ b/docker/start_worker.sh @@ -2,9 +2,6 @@ echo "Starting InvenTree worker..." -# Activate virtual environment -source ./env/bin/activate - sleep 5 # Wait for the database to be ready From 91b6f98f95484bdce2159a105516937685d72ba2 Mon Sep 17 00:00:00 2001 From: Oliver Walters <oliver.henry.walters@gmail.com> Date: Sat, 10 Apr 2021 22:08:36 +1000 Subject: [PATCH 090/111] Update directory structure to match docker config --- InvenTree/InvenTree/settings.py | 4 ++-- InvenTree/config_template.yaml | 6 +++--- docker/Dockerfile | 4 +++- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/InvenTree/InvenTree/settings.py b/InvenTree/InvenTree/settings.py index 9a6a4d59ae..8b5400e374 100644 --- a/InvenTree/InvenTree/settings.py +++ b/InvenTree/InvenTree/settings.py @@ -182,7 +182,7 @@ STATIC_URL = '/static/' STATIC_ROOT = os.path.abspath( get_setting( 'INVENTREE_STATIC_ROOT', - CONFIG.get('static_root', os.path.join(BASE_DIR, 'static')) + CONFIG.get('static_root', '/home/inventree/static') ) ) @@ -200,7 +200,7 @@ MEDIA_URL = '/media/' MEDIA_ROOT = os.path.abspath( get_setting( 'INVENTREE_MEDIA_ROOT', - CONFIG.get('media_root', os.path.join(BASE_DIR, 'media')) + CONFIG.get('media_root', '/home/inventree/data/media') ) ) diff --git a/InvenTree/config_template.yaml b/InvenTree/config_template.yaml index 9e18adb759..a64e6d42c0 100644 --- a/InvenTree/config_template.yaml +++ b/InvenTree/config_template.yaml @@ -95,9 +95,9 @@ cors: # - https://sub.example.com # MEDIA_ROOT is the local filesystem location for storing uploaded files -# By default, it is stored under /home/inventree +# By default, it is stored under /home/inventree/data/media # Use environment variable INVENTREE_MEDIA_ROOT -media_root: '/home/inventree/media' +media_root: '/home/inventree/data/media' # STATIC_ROOT is the local filesystem location for storing static files # By default, it is stored under /home/inventree @@ -116,7 +116,7 @@ static_root: '/home/inventree/static' # Set the backup_dir parameter to store backup files in a specific location # If unspecified, the local user's temp directory will be used # Use environment variable INVENTREE_BACKUP_DIR -backup_dir: '/home/inventree/backup/' +backup_dir: '/home/inventree/data/backup/' # Permit custom authentication backends #authentication_backends: diff --git a/docker/Dockerfile b/docker/Dockerfile index eefc700cc7..5ab396ca44 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -19,7 +19,7 @@ ENV INVENTREE_LOG_LEVEL="INFO" ENV INVENTREE_SRC_DIR="${INVENTREE_HOME}/src" ENV INVENTREE_MNG_DIR="${INVENTREE_SRC_DIR}/InvenTree" ENV INVENTREE_DATA_DIR="${INVENTREE_HOME}/data" -ENV INVENTREE_STATIC_ROOT="${INVENTREE_DATA_DIR}/static" +ENV INVENTREE_STATIC_ROOT="${INVENTREE_HOME}/static" ENV INVENTREE_MEDIA_ROOT="${INVENTREE_DATA_DIR}/media" ENV INVENTREE_BACKUP_DIR="${INVENTREE_DATA_DIR}/backup" @@ -48,6 +48,8 @@ LABEL org.label-schema.schema-version="1.0" \ RUN addgroup -S inventreegroup && adduser -S inventree -G inventreegroup WORKDIR ${INVENTREE_HOME} +RUN mkdir ${INVENTREE_STATIC_ROOT} + # Install required system packages RUN apk add --no-cache git make bash \ gcc libgcc g++ libstdc++ \ From 5d9e2735598d2f350ab8d7ef50f094defc7bda46 Mon Sep 17 00:00:00 2001 From: Oliver Walters <oliver.henry.walters@gmail.com> Date: Sat, 10 Apr 2021 22:25:07 +1000 Subject: [PATCH 091/111] Adds nxinx service --- docker/Dockerfile | 5 ++++- docker/docker-compose.yml | 24 ++++++++++++++++++++++-- docker/nginx/Dockerfile | 14 ++++++++++++++ docker/nginx/nginx.conf | 21 +++++++++++++++++++++ 4 files changed, 61 insertions(+), 3 deletions(-) create mode 100644 docker/nginx/Dockerfile create mode 100644 docker/nginx/nginx.conf diff --git a/docker/Dockerfile b/docker/Dockerfile index 5ab396ca44..3d89ef9349 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -88,5 +88,8 @@ COPY start_worker.sh ${INVENTREE_HOME}/start_worker.sh RUN chmod 755 ${INVENTREE_HOME}/start_server.sh RUN chmod 755 ${INVENTREE_HOME}/start_worker.sh +# exec commands should be executed from the "src" directory +WORKDIR ${INVENTREE_SRC_DIR} + # Let us begin -CMD ["bash", "./start_server.sh"] +CMD ["bash", "../start_server.sh"] diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index 839128738d..14f6108527 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -34,12 +34,13 @@ services: branch: "django-q" image: inventree/inventree:latest container_name: inventree_server - ports: - - "8080:8080" + expose: + - 8080 depends_on: - db volumes: - data:/home/inventree/data + - static:/home/inventree/static environment: - INVENTREE_DB_ENGINE=postgresql - INVENTREE_DB_NAME=inventree @@ -49,6 +50,21 @@ services: - INVENTREE_DB_HOST=db restart: unless-stopped + # nginx acts as a reverse proxy + # static files are served by nginx + # web requests are redirected to gunicorn + nginx: + build: + context: nginx + container_name: inventree_proxy + depends_on: + - inventree + ports: + - 1337:80 + volumes: + - static:/home/inventree/static + + # background worker process handles long-running or periodic tasks worker: build: context: . @@ -63,6 +79,7 @@ services: - inventree volumes: - data:/home/inventree/data + - static:/home/inventree/static environment: - INVENTREE_DB_ENGINE=postgresql - INVENTREE_DB_NAME=inventree @@ -73,6 +90,9 @@ services: restart: unless-stopped volumes: + # Static files, shared between containers + static: + # Persistent data, stored externally data: driver: local driver_opts: diff --git a/docker/nginx/Dockerfile b/docker/nginx/Dockerfile new file mode 100644 index 0000000000..e754597f02 --- /dev/null +++ b/docker/nginx/Dockerfile @@ -0,0 +1,14 @@ +FROM nginx:1.19.0-alpine + +# Create user account +RUN addgroup -S inventreegroup && adduser -S inventree -G inventreegroup + +ENV HOME=/home/inventree +WORKDIR $HOME + +# Create the "static" volume directory +RUN mkdir $HOME/static + +RUN rm /etc/nginx/conf.d/default.conf +COPY nginx.conf /etc/nginx/conf.d + diff --git a/docker/nginx/nginx.conf b/docker/nginx/nginx.conf new file mode 100644 index 0000000000..0f25f51674 --- /dev/null +++ b/docker/nginx/nginx.conf @@ -0,0 +1,21 @@ +upstream inventree { + server inventree:8080; +} + +server { + + listen 80; + + location / { + proxy_pass http://inventree; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header Host $host; + proxy_redirect off; + client_max_body_size 100M; + } + + location /static/ { + alias /home/inventree/static/; + } + +} \ No newline at end of file From 8f626d305e771e937bd40d1e431f8715669dae44 Mon Sep 17 00:00:00 2001 From: Oliver Walters <oliver.henry.walters@gmail.com> Date: Sat, 10 Apr 2021 22:35:10 +1000 Subject: [PATCH 092/111] Fix location of entrypoint scripts --- docker/Dockerfile | 12 +++++++----- docker/docker-compose.yml | 5 +++-- docker/start_worker.sh | 3 +-- tasks.py | 8 ++++++++ 4 files changed, 19 insertions(+), 9 deletions(-) diff --git a/docker/Dockerfile b/docker/Dockerfile index 3d89ef9349..91870b6551 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -82,14 +82,16 @@ RUN pip install --no-cache-dir -U -r ${INVENTREE_SRC_DIR}/requirements.txt COPY gunicorn.conf.py ${INVENTREE_HOME}/gunicorn.conf.py # Copy startup scripts -COPY start_server.sh ${INVENTREE_HOME}/start_server.sh -COPY start_worker.sh ${INVENTREE_HOME}/start_worker.sh +COPY start_server.sh ${INVENTREE_SRC_DIR}/start_server.sh +COPY start_worker.sh ${INVENTREE_SRC_DIR}/start_worker.sh -RUN chmod 755 ${INVENTREE_HOME}/start_server.sh -RUN chmod 755 ${INVENTREE_HOME}/start_worker.sh +RUN chmod 755 ${INVENTREE_SRC_DIR}/start_server.sh +RUN chmod 755 ${INVENTREE_SRC_DIR}/start_worker.sh # exec commands should be executed from the "src" directory WORKDIR ${INVENTREE_SRC_DIR} +USER inventree + # Let us begin -CMD ["bash", "../start_server.sh"] +CMD ["bash", "./start_server.sh"] diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index 14f6108527..265f8b7c1c 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -2,9 +2,9 @@ version: "3.8" # Docker compose recipe for InvenTree # - Runs PostgreSQL as the database backend -# - Serves web data using Gunicorn -# - Runs the background worker process +# - Runs Gunicorn as the web server # - Runs nginx as a reverse proxy +# - Runs the background worker process services: # Database service @@ -60,6 +60,7 @@ services: depends_on: - inventree ports: + # Change "1337" to the port where you want InvenTree web server to be available - 1337:80 volumes: - static:/home/inventree/static diff --git a/docker/start_worker.sh b/docker/start_worker.sh index 64c8d75ee4..7d0921a7af 100644 --- a/docker/start_worker.sh +++ b/docker/start_worker.sh @@ -5,8 +5,7 @@ echo "Starting InvenTree worker..." sleep 5 # Wait for the database to be ready -cd src/InvenTree - +cd $INVENTREE_MNG_DIR python manage.py wait_for_db sleep 10 diff --git a/tasks.py b/tasks.py index c6d7dd0173..5f9cff6c7d 100644 --- a/tasks.py +++ b/tasks.py @@ -117,6 +117,14 @@ def check(c): manage(c, "check") +@task +def wait(c): + """ + Wait until the database connection is ready + """ + + manage(c, "wait_for_db") + @task def migrate(c): """ From 5a168abbfee3db4791f00f943a34e12d3b5dbfeb Mon Sep 17 00:00:00 2001 From: Oliver Walters <oliver.henry.walters@gmail.com> Date: Sat, 10 Apr 2021 22:42:08 +1000 Subject: [PATCH 093/111] Separated docker file into separate directory --- docker/docker-compose.yml | 11 +++++-- docker/{ => inventree}/Dockerfile | 0 docker/{ => inventree}/gunicorn.conf.py | 0 docker/{ => inventree}/start_server.sh | 0 docker/{ => inventree}/start_worker.sh | 0 docker/supervisord.conf | 43 ------------------------- 6 files changed, 9 insertions(+), 45 deletions(-) rename docker/{ => inventree}/Dockerfile (100%) rename docker/{ => inventree}/gunicorn.conf.py (100%) rename docker/{ => inventree}/start_server.sh (100%) rename docker/{ => inventree}/start_worker.sh (100%) delete mode 100644 docker/supervisord.conf diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index 265f8b7c1c..e3a86b195f 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -6,6 +6,13 @@ version: "3.8" # - Runs nginx as a reverse proxy # - Runs the background worker process +# --------------------------------- +# IMPORTANT - READ BEFORE STARTING! +# --------------------------------- +# Before running, ensure that you change the "/path/to/data" directory, +# specified in the "volumes" section at the end of this file. +# This path determines where the InvenTree data will be stored! + services: # Database service # Use PostgreSQL as the database backend @@ -28,7 +35,7 @@ services: # Uses gunicorn as the web server inventree: build: - context: . + context: inventree args: repository: "https://github.com/SchrodingersGat/InvenTree.git" branch: "django-q" @@ -68,7 +75,7 @@ services: # background worker process handles long-running or periodic tasks worker: build: - context: . + context: inventree args: repository: "https://github.com/SchrodingersGat/InvenTree.git" branch: "django-q" diff --git a/docker/Dockerfile b/docker/inventree/Dockerfile similarity index 100% rename from docker/Dockerfile rename to docker/inventree/Dockerfile diff --git a/docker/gunicorn.conf.py b/docker/inventree/gunicorn.conf.py similarity index 100% rename from docker/gunicorn.conf.py rename to docker/inventree/gunicorn.conf.py diff --git a/docker/start_server.sh b/docker/inventree/start_server.sh similarity index 100% rename from docker/start_server.sh rename to docker/inventree/start_server.sh diff --git a/docker/start_worker.sh b/docker/inventree/start_worker.sh similarity index 100% rename from docker/start_worker.sh rename to docker/inventree/start_worker.sh diff --git a/docker/supervisord.conf b/docker/supervisord.conf deleted file mode 100644 index b99c48edcf..0000000000 --- a/docker/supervisord.conf +++ /dev/null @@ -1,43 +0,0 @@ -; # Supervisor Config File (for docker build) -; -; This config file is specific to the InvenTree docker build! -; -; There are two separate processes which must be managed: -; -; ## Web Server -; The InvenTree server must be launched and managed as a process -; The recommended way to handle the web server is to use gunicorn -; -; ## Background Tasks -; A background task manager processes long-running and periodic tasks -; InvenTree uses django-q for this purpose - -[supervisord] -user=inventree -nodaemon=true - -[supervisorctl] -serverurl=unix:///var/run/supervisor.sock - -[program:inventree-server] -user=inventree -directory=%(ENV_INVENTREE_SRC_DIR)s/InvenTree -command=%(ENV_INVENTREE_VENV)s/bin/gunicorn -c %(ENV_INVENTREE_HOME)s/gunicorn.conf.py InvenTree.wsgi -b 127.0.0.1:8080 -startsecs=10 -autostart=true -autorestart=true -startretries=3 -stdout_logfile=/dev/fd/1 -stdout_logfile_maxbytes=0 -redirect_stderr=true - -[program:inventree-cluster] -user=inventree -directory=%(ENV_INVENTREE_SRC_DIR)s/InvenTree -command=%(ENV_INVENTREE_VENV)s/bin/python DJANGO_SETTINGS_FILE=InvenTree.settings manage.py qcluster -startsecs=10 -autostart=true -autorestart=true -stdout_logfile=/dev/fd/1 -stdout_logfile_maxbytes=0 -redirect_stderr=true \ No newline at end of file From 3da5505b58027ed9caefd6c6b8f83d6ed2920d18 Mon Sep 17 00:00:00 2001 From: Oliver Walters <oliver.henry.walters@gmail.com> Date: Sat, 10 Apr 2021 22:44:37 +1000 Subject: [PATCH 094/111] Fix build workflow --- .github/workflows/docker.yaml | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/.github/workflows/docker.yaml b/.github/workflows/docker.yaml index 34ca585c98..a5165861f2 100644 --- a/.github/workflows/docker.yaml +++ b/.github/workflows/docker.yaml @@ -6,11 +6,19 @@ on: ["push", "pull_request"] jobs: - docker: + inventree: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - - name: Build Docker Image - run: cd docker && docker build . --tag inventree:$(date +%s) - \ No newline at end of file + - name: Build Server Image + run: cd docker/inventree && docker build . --tag inventree:$(date +%s) + + nginx: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + - name: Build nginx Image + run: cd docker/nginx && docker build . --tag nxinx:$(date +%s) + \ No newline at end of file From 0e1b647e7bb627a6cdaa5dccad26c4d1b6a01d69 Mon Sep 17 00:00:00 2001 From: Oliver Walters <oliver.henry.walters@gmail.com> Date: Sat, 10 Apr 2021 22:47:30 +1000 Subject: [PATCH 095/111] Remove mariadb test (uses the same backend as mysql!) --- .github/workflows/mariadb.yaml | 48 ---------------------------------- README.md | 1 - 2 files changed, 49 deletions(-) delete mode 100644 .github/workflows/mariadb.yaml diff --git a/.github/workflows/mariadb.yaml b/.github/workflows/mariadb.yaml deleted file mode 100644 index f976cfa088..0000000000 --- a/.github/workflows/mariadb.yaml +++ /dev/null @@ -1,48 +0,0 @@ -name: MariaDB - -on: ["push", "pull_request"] - -jobs: - - test: - runs-on: ubuntu-latest - - env: - # Database backend configuration - INVENTREE_DB_ENGINE: django.db.backends.mysql - INVENTREE_DB_NAME: inventree - INVENTREE_DB_USER: root - INVENTREE_DB_PASSWORD: password - INVENTREE_DB_HOST: '127.0.0.1' - INVENTREE_DB_PORT: 3306 - INVENTREE_DEBUG: info - INVENTREE_MEDIA_ROOT: ./media - INVENTREE_STATIC_ROOT: ./static - - services: - mariadb: - image: mariadb:latest - env: - MYSQL_ALLOW_EMPTY_PASSWORD: yes - MYSQL_DATABASE: inventree - MYSQL_USER: inventree - MYSQL_PASSWORD: password - MYSQL_ROOT_PASSWORD: password - ports: - - 3306:3306 - - steps: - - name: Checkout Code - uses: actions/checkout@v2 - - name: Setup Python - uses: actions/setup-python@v2 - with: - python-version: 3.7 - - name: Install Dependencies - run: | - sudo apt-get install mysql-server libmysqlclient-dev - pip3 install invoke - pip3 install mysqlclient - invoke install - - name: Run Tests - run: invoke test diff --git a/README.md b/README.md index ef0556cceb..2a622266d7 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,6 @@    -  <img src="images/logo/inventree.png" alt="InvenTree" width="128"/> From c9021fe9919854cad255280ace7cb0bab3922cb5 Mon Sep 17 00:00:00 2001 From: Oliver Walters <oliver.henry.walters@gmail.com> Date: Sat, 10 Apr 2021 22:48:23 +1000 Subject: [PATCH 096/111] Simplify docker build workflow --- .github/workflows/docker.yaml | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/.github/workflows/docker.yaml b/.github/workflows/docker.yaml index a5165861f2..c9f8a69654 100644 --- a/.github/workflows/docker.yaml +++ b/.github/workflows/docker.yaml @@ -6,19 +6,13 @@ on: ["push", "pull_request"] jobs: - inventree: + docker: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - name: Build Server Image run: cd docker/inventree && docker build . --tag inventree:$(date +%s) - - nginx: - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v2 - name: Build nginx Image run: cd docker/nginx && docker build . --tag nxinx:$(date +%s) \ No newline at end of file From 2e8d3b64244ae9b97537a233b081431edb44552c Mon Sep 17 00:00:00 2001 From: Oliver Walters <oliver.henry.walters@gmail.com> Date: Sun, 11 Apr 2021 13:22:16 +1000 Subject: [PATCH 097/111] Fix for tasks.py (??) --- deploy/{inventree.conf => supervisord.conf} | 6 ++++++ tasks.py | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) rename deploy/{inventree.conf => supervisord.conf} (86%) diff --git a/deploy/inventree.conf b/deploy/supervisord.conf similarity index 86% rename from deploy/inventree.conf rename to deploy/supervisord.conf index 782b6ae2f6..d4f6ce4419 100644 --- a/deploy/inventree.conf +++ b/deploy/supervisord.conf @@ -13,7 +13,12 @@ [supervisord] ; Change this path if log files are stored elsewhere logfile=/home/inventree/log/supervisor.log +user=inventree +[supervisorctl] +serverurl=unix:///var/run/supervisor.sock + +; InvenTree Web Server Process [program:inventree-server] user=inventree directory=/home/inventree/src/InvenTree @@ -26,6 +31,7 @@ startretries=3 stderr_logfile=/home/inventree/log/server.err.log stdout_logfile=/home/inventree/log/server.out.log +; InvenTree Background Worker Process [program:inventree-cluster] user=inventree directory=/home/inventree/src/InvenTree diff --git a/tasks.py b/tasks.py index 5f9cff6c7d..ebbe199865 100644 --- a/tasks.py +++ b/tasks.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -from invoke import task +from invoke import ctask as task from shutil import copyfile import os From 78bcbe271a4be1afb7fa646d1bd75bd57eb61cf1 Mon Sep 17 00:00:00 2001 From: Oliver Walters <oliver.henry.walters@gmail.com> Date: Sun, 11 Apr 2021 13:45:56 +1000 Subject: [PATCH 098/111] Update supervisor conf file --- deploy/supervisord.conf | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/deploy/supervisord.conf b/deploy/supervisord.conf index d4f6ce4419..88a5af3e5c 100644 --- a/deploy/supervisord.conf +++ b/deploy/supervisord.conf @@ -16,7 +16,9 @@ logfile=/home/inventree/log/supervisor.log user=inventree [supervisorctl] -serverurl=unix:///var/run/supervisor.sock + +[inet_http_server] +port = 127.0.0.1:9001 ; InvenTree Web Server Process [program:inventree-server] From f6f3815f31149ce9a51c2d7533f58e8c1f7182e0 Mon Sep 17 00:00:00 2001 From: Oliver Walters <oliver.henry.walters@gmail.com> Date: Sun, 11 Apr 2021 13:58:59 +1000 Subject: [PATCH 099/111] Include worker status in main API call --- InvenTree/InvenTree/api.py | 2 ++ InvenTree/InvenTree/context.py | 2 +- InvenTree/InvenTree/status.py | 6 +++--- docker/docker-compose.yml | 3 ++- 4 files changed, 8 insertions(+), 5 deletions(-) diff --git a/InvenTree/InvenTree/api.py b/InvenTree/InvenTree/api.py index c7386e8fbc..a006050694 100644 --- a/InvenTree/InvenTree/api.py +++ b/InvenTree/InvenTree/api.py @@ -19,6 +19,7 @@ from rest_framework.views import APIView from .views import AjaxView from .version import inventreeVersion, inventreeApiVersion, inventreeInstanceName +from .status import is_worker_running from plugins import plugins as inventree_plugins @@ -44,6 +45,7 @@ class InfoView(AjaxView): 'version': inventreeVersion(), 'instance': inventreeInstanceName(), 'apiVersion': inventreeApiVersion(), + 'worker_running': is_worker_running(), } return JsonResponse(data) diff --git a/InvenTree/InvenTree/context.py b/InvenTree/InvenTree/context.py index 43e8b904b9..3e9af2f751 100644 --- a/InvenTree/InvenTree/context.py +++ b/InvenTree/InvenTree/context.py @@ -31,7 +31,7 @@ def health_status(request): request._inventree_health_status = True status = { - 'django_q_running': InvenTree.status.is_q_cluster_running(), + 'django_q_running': InvenTree.status.is_worker_running(), } all_healthy = True diff --git a/InvenTree/InvenTree/status.py b/InvenTree/InvenTree/status.py index 42160927b0..88acc69a7a 100644 --- a/InvenTree/InvenTree/status.py +++ b/InvenTree/InvenTree/status.py @@ -14,9 +14,9 @@ from django_q.monitor import Stat logger = logging.getLogger("inventree") -def is_q_cluster_running(**kwargs): +def is_worker_running(**kwargs): """ - Return True if at least one cluster worker is running + Return True if the background worker process is oprational """ clusters = Stat.get_all() @@ -52,7 +52,7 @@ def check_system_health(**kwargs): result = True - if not is_q_cluster_running(**kwargs): + if not is_worker_running(**kwargs): result = False logger.warning(_("Background worker check failed")) diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index e3a86b195f..8ef272ece2 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -107,4 +107,5 @@ volumes: type: none o: bind # This directory specified where InvenTree data are stored "outside" the docker containers - device: c:/abcdef \ No newline at end of file + # Change this path to a local system path where you want InvenTree data stored + device: /path/to/data From 44fe5721e0e4fee4eab9ef1b0560ece11236d8c2 Mon Sep 17 00:00:00 2001 From: Oliver Walters <oliver.henry.walters@gmail.com> Date: Sun, 11 Apr 2021 14:05:55 +1000 Subject: [PATCH 100/111] Disgusting hack for tasks.py --- tasks.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/tasks.py b/tasks.py index ebbe199865..895f3183ce 100644 --- a/tasks.py +++ b/tasks.py @@ -1,11 +1,14 @@ # -*- coding: utf-8 -*- -from invoke import ctask as task from shutil import copyfile - import os import sys +try: + from invoke import ctask as task +except: + from invoke import task + def apps(): """ From 5f9236d280faeef96c474a57c3b8930dd2748a93 Mon Sep 17 00:00:00 2001 From: Oliver Walters <oliver.henry.walters@gmail.com> Date: Sun, 11 Apr 2021 14:46:40 +1000 Subject: [PATCH 101/111] Updates to docker files --- docker/docker-compose.yml | 3 ++- docker/inventree/Dockerfile | 2 -- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index 8ef272ece2..c802444c79 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -64,6 +64,7 @@ services: build: context: nginx container_name: inventree_proxy + image: inventree/nginx:latest depends_on: - inventree ports: @@ -80,7 +81,7 @@ services: repository: "https://github.com/SchrodingersGat/InvenTree.git" branch: "django-q" entrypoint: ./start_worker.sh - image: inventree/worker:latest + image: inventree/inventree:latest container_name: inventree_worker depends_on: - db diff --git a/docker/inventree/Dockerfile b/docker/inventree/Dockerfile index 91870b6551..fcba19e964 100644 --- a/docker/inventree/Dockerfile +++ b/docker/inventree/Dockerfile @@ -91,7 +91,5 @@ RUN chmod 755 ${INVENTREE_SRC_DIR}/start_worker.sh # exec commands should be executed from the "src" directory WORKDIR ${INVENTREE_SRC_DIR} -USER inventree - # Let us begin CMD ["bash", "./start_server.sh"] From 34e95ab70cf533a12892e5f0497f7965decfc5f8 Mon Sep 17 00:00:00 2001 From: Oliver <oliver.henry.walters@gmail.com> Date: Sun, 11 Apr 2021 14:49:41 +1000 Subject: [PATCH 102/111] Update version.py --- InvenTree/InvenTree/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/InvenTree/InvenTree/version.py b/InvenTree/InvenTree/version.py index d7bcd4f7ed..1a28900905 100644 --- a/InvenTree/InvenTree/version.py +++ b/InvenTree/InvenTree/version.py @@ -7,7 +7,7 @@ import django import common.models -INVENTREE_SW_VERSION = "0.1.8 pre" +INVENTREE_SW_VERSION = "0.2.0 pre" # Increment this number whenever there is a significant change to the API that any clients need to know about INVENTREE_API_VERSION = 2 From b490c5d03549fb11213ad5bfd0bf269b64b370dd Mon Sep 17 00:00:00 2001 From: Oliver Walters <oliver.henry.walters@gmail.com> Date: Sun, 11 Apr 2021 15:08:13 +1000 Subject: [PATCH 103/111] Add new docker workflow for publising docker images on release --- .../{docker.yaml => docker_build.yaml} | 0 .github/workflows/docker_publish.yaml | 38 +++++++++++++++++++ 2 files changed, 38 insertions(+) rename .github/workflows/{docker.yaml => docker_build.yaml} (100%) create mode 100644 .github/workflows/docker_publish.yaml diff --git a/.github/workflows/docker.yaml b/.github/workflows/docker_build.yaml similarity index 100% rename from .github/workflows/docker.yaml rename to .github/workflows/docker_build.yaml diff --git a/.github/workflows/docker_publish.yaml b/.github/workflows/docker_publish.yaml new file mode 100644 index 0000000000..3b9ad13a98 --- /dev/null +++ b/.github/workflows/docker_publish.yaml @@ -0,0 +1,38 @@ +# Publish docker images to dockerhub + +name: Docker Publish + +on: + release: + types: [published] + +jobs: + server_image: + name: Push InvenTree web server image to dockerhub + runs-on: ubuntu-latest + steps: + - name: Check out repo + uses: actions/checkout@v2 + - name: Push to Docker Hub + uses: docker/build-push-action@v1 + with: + username: ${{ secrets.DOCKER_USERNAME }} + password: ${{ secrets.DOCKER_PASSWORD }} + repository: inventree/inventree + tag_with_ref: true + context: docker/inventree + + nginx_image: + name: Push InvenTree nginx image to dockerhub + runs-on: ubuntu-latest + steps: + - name: Check out repo + uses: actions/checkout@v2 + - name: Push to Docker Hub + uses: docker/build-push-action@v1 + with: + username: ${{ secrets.DOCKER_USERNAME }} + password: ${{ secrets.DOCKER_PASSWORD }} + repository: inventree/nginx + tag_with_ref: true + context: docker/nginx From 8f07efa4e3de7657e90f599959ad64b231f65e87 Mon Sep 17 00:00:00 2001 From: Oliver Walters <oliver.henry.walters@gmail.com> Date: Sun, 11 Apr 2021 15:15:11 +1000 Subject: [PATCH 104/111] Add dockerhub badge --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 2a622266d7..28169a7f90 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,5 @@ [](https://opensource.org/licenses/MIT) +[](https://hub.docker.com/inventree/inventree) [](https://coveralls.io/github/inventree/InvenTree)   From c2f85b0447d61e82da79e545c9e8a3b81b5c51f7 Mon Sep 17 00:00:00 2001 From: Oliver Walters <oliver.henry.walters@gmail.com> Date: Sun, 11 Apr 2021 15:25:32 +1000 Subject: [PATCH 105/111] docker-compose tweaks --- docker/docker-compose.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index a01f152b07..90b5fa2668 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -19,8 +19,8 @@ services: # Note: this can be changed to a different backend, # just make sure that you change the INVENTREE_DB_xxx vars below db: + container_name: db image: postgres - container_name: inventree_db ports: - 5432/tcp environment: @@ -34,8 +34,8 @@ services: # InvenTree web server services # Uses gunicorn as the web server inventree: + container_name: server image: inventree/inventree:latest - container_name: inventree_server expose: - 8080 depends_on: @@ -56,7 +56,7 @@ services: # static files are served by nginx # web requests are redirected to gunicorn nginx: - container_name: inventree_proxy + container_name: nginx image: inventree/nginx:latest depends_on: - inventree @@ -68,9 +68,9 @@ services: # background worker process handles long-running or periodic tasks worker: - entrypoint: ./start_worker.sh + container_name: worker image: inventree/inventree:latest - container_name: inventree_worker + entrypoint: ./start_worker.sh depends_on: - db - inventree From 4cf0339393baf0c330ef120efe235b9be84b216f Mon Sep 17 00:00:00 2001 From: Oliver <oliver.henry.walters@gmail.com> Date: Sun, 11 Apr 2021 15:38:52 +1000 Subject: [PATCH 106/111] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 28169a7f90..0a8cfa1749 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ [](https://hub.docker.com/inventree/inventree) [](https://coveralls.io/github/inventree/InvenTree)  - +    From 7c9ad3f4062234763cb8c5fb043b36e24c64f44f Mon Sep 17 00:00:00 2001 From: Oliver <oliver.henry.walters@gmail.com> Date: Sun, 11 Apr 2021 15:39:16 +1000 Subject: [PATCH 107/111] Update version.py --- InvenTree/InvenTree/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/InvenTree/InvenTree/version.py b/InvenTree/InvenTree/version.py index 7bfb752b63..177db4d93c 100644 --- a/InvenTree/InvenTree/version.py +++ b/InvenTree/InvenTree/version.py @@ -8,7 +8,7 @@ import re import common.models -INVENTREE_SW_VERSION = "0.2.0 pre" +INVENTREE_SW_VERSION = "0.2.0" # Increment this number whenever there is a significant change to the API that any clients need to know about INVENTREE_API_VERSION = 2 From effd5472600ca3876ed1dbbf1eff668f8e226bc1 Mon Sep 17 00:00:00 2001 From: Oliver <oliver.henry.walters@gmail.com> Date: Sun, 11 Apr 2021 15:39:53 +1000 Subject: [PATCH 108/111] Update version.py --- InvenTree/InvenTree/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/InvenTree/InvenTree/version.py b/InvenTree/InvenTree/version.py index 177db4d93c..b79323e1e7 100644 --- a/InvenTree/InvenTree/version.py +++ b/InvenTree/InvenTree/version.py @@ -8,7 +8,7 @@ import re import common.models -INVENTREE_SW_VERSION = "0.2.0" +INVENTREE_SW_VERSION = "0.2.1 pre" # Increment this number whenever there is a significant change to the API that any clients need to know about INVENTREE_API_VERSION = 2 From 029808a986abb161d7b38d2cccb751c1d6d5e32c Mon Sep 17 00:00:00 2001 From: Oliver Walters <oliver.henry.walters@gmail.com> Date: Sun, 11 Apr 2021 15:45:17 +1000 Subject: [PATCH 109/111] Fix workflow for publishing docker files --- .github/workflows/docker_publish.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/docker_publish.yaml b/.github/workflows/docker_publish.yaml index 3b9ad13a98..6870754ad3 100644 --- a/.github/workflows/docker_publish.yaml +++ b/.github/workflows/docker_publish.yaml @@ -20,7 +20,7 @@ jobs: password: ${{ secrets.DOCKER_PASSWORD }} repository: inventree/inventree tag_with_ref: true - context: docker/inventree + dockerfile: docker/inventree/Dockerfile nginx_image: name: Push InvenTree nginx image to dockerhub @@ -35,4 +35,4 @@ jobs: password: ${{ secrets.DOCKER_PASSWORD }} repository: inventree/nginx tag_with_ref: true - context: docker/nginx + dockerfile: docker/nginx/Dockerfile From 85c9bc1b81a0dd749d0cf36dbad7238147410543 Mon Sep 17 00:00:00 2001 From: Oliver Walters <oliver.henry.walters@gmail.com> Date: Sun, 11 Apr 2021 18:56:35 +1000 Subject: [PATCH 110/111] Adds detail endpoint for PartParameter model --- InvenTree/part/api.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/InvenTree/part/api.py b/InvenTree/part/api.py index a245f9d67c..54222b7e67 100644 --- a/InvenTree/part/api.py +++ b/InvenTree/part/api.py @@ -735,6 +735,15 @@ class PartParameterList(generics.ListCreateAPIView): ] +class PartParameterDetail(generics.RetrieveUpdateDestroyAPIView): + """ + API endpoint for detail view of a single PartParameter object + """ + + queryset = PartParameter.objects.all() + serializer_class = part_serializers.PartParameterSerializer + + class BomList(generics.ListCreateAPIView): """ API endpoint for accessing a list of BomItem objects. @@ -942,6 +951,8 @@ part_api_urls = [ # Base URL for PartParameter API endpoints url(r'^parameter/', include([ url(r'^template/$', PartParameterTemplateList.as_view(), name='api-part-param-template-list'), + + url(r'^(?P<pk>\d+)/', PartParameterDetail.as_view(), name='api-part-param-detail'), url(r'^.*$', PartParameterList.as_view(), name='api-part-param-list'), ])), From 8a06eaa40d97293c56d1f1bcf45f129dd66d95b9 Mon Sep 17 00:00:00 2001 From: Oliver Walters <oliver.henry.walters@gmail.com> Date: Sun, 11 Apr 2021 19:28:39 +1000 Subject: [PATCH 111/111] Unit testing --- InvenTree/part/fixtures/params.yaml | 21 ++++++ InvenTree/part/test_api.py | 103 ++++++++++++++++++++++++++++ 2 files changed, 124 insertions(+) diff --git a/InvenTree/part/fixtures/params.yaml b/InvenTree/part/fixtures/params.yaml index e65c7335cc..3056be473b 100644 --- a/InvenTree/part/fixtures/params.yaml +++ b/InvenTree/part/fixtures/params.yaml @@ -33,6 +33,27 @@ template: 1 data: 12 +- model: part.PartParameter + pk: 3 + fields: + part: 3 + template: 1 + data: 12 + +- model: part.PartParameter + pk: 4 + fields: + part: 3 + template: 2 + data: 12 + +- model: part.PartParameter + pk: 5 + fields: + part: 3 + template: 3 + data: 12 + # Add some template parameters to categories (requires category.yaml) - model: part.PartCategoryParameterTemplate pk: 1 diff --git a/InvenTree/part/test_api.py b/InvenTree/part/test_api.py index faadf26c15..ed88e1dd55 100644 --- a/InvenTree/part/test_api.py +++ b/InvenTree/part/test_api.py @@ -325,3 +325,106 @@ class PartAPIAggregationTest(InvenTreeAPITestCase): self.assertEqual(data['in_stock'], 1100) self.assertEqual(data['stock_item_count'], 105) + + +class PartParameterTest(InvenTreeAPITestCase): + """ + Tests for the ParParameter API + """ + + superuser = True + + fixtures = [ + 'category', + 'part', + 'location', + 'params', + ] + + def setUp(self): + + super().setUp() + + def test_list_params(self): + """ + Test for listing part parameters + """ + + url = reverse('api-part-param-list') + + response = self.client.get(url, format='json') + + self.assertEqual(len(response.data), 5) + + # Filter by part + response = self.client.get( + url, + { + 'part': 3, + }, + format='json' + ) + + self.assertEqual(len(response.data), 3) + + # Filter by template + response = self.client.get( + url, + { + 'template': 1, + }, + format='json', + ) + + self.assertEqual(len(response.data), 3) + + def test_create_param(self): + """ + Test that we can create a param via the API + """ + + url = reverse('api-part-param-list') + + response = self.client.post( + url, + { + 'part': '2', + 'template': '3', + 'data': 70 + } + ) + + self.assertEqual(response.status_code, 201) + + response = self.client.get(url, format='json') + + self.assertEqual(len(response.data), 6) + + def test_param_detail(self): + """ + Tests for the PartParameter detail endpoint + """ + + url = reverse('api-part-param-detail', kwargs={'pk': 5}) + + response = self.client.get(url) + + self.assertEqual(response.status_code, 200) + + data = response.data + + self.assertEqual(data['pk'], 5) + self.assertEqual(data['part'], 3) + self.assertEqual(data['data'], '12') + + # PATCH data back in + response = self.client.patch(url, {'data': '15'}, format='json') + + self.assertEqual(response.status_code, 200) + + # Check that the data changed! + response = self.client.get(url, format='json') + + data = response.data + + self.assertEqual(data['data'], '15')