Compare commits

..

25 Commits

Author SHA1 Message Date
Florian du Garage Num
fa053ac7ae remove hard-coded uid 1000
Some checks failed
Run tests / tests (3.9) (push) Has been cancelled
Sync with private repo / sync (push) Has been cancelled
Run tests / tests (3.12) (push) Has been cancelled
2025-09-30 21:46:06 +02:00
Muhammad Labeeb
18d1825de7
feat: migrate from pylint/black to ruff (#104)
* feat: migrate from pylint/black to ruff

* test: verify python package distribution build when running make test
2025-08-28 18:59:56 +05:00
Ahmed Khalid
c6a0d16a76
Merge pull request #102 from overhangio/teak 2025-06-27 15:57:02 +05:00
Muhammad Labeeb
944b432ca4 v20.0.0 2025-06-05 18:10:15 +05:00
Danyal Faheem
7a31ce81fd
chore: replace site-configuration script with management command (#101)
* chore: replace site-configuration script with management command
The site-configuration script was used to set the value of the COURSE_CATALOG_API_URL
However, this script is being removed in tutor core as it is no longer required
For this reason, we migrate to the provided create_or_update_site_configuration management command in edx-platform
2025-04-22 15:23:18 +05:00
Syed Muhammad Dawoud Sheraz Ali
b189574e03
build: Add hatch_build in sdist to fix installation issues (#100) 2025-03-12 16:14:02 +05:00
Muhammad Faraz Maqsood
5c4daa9318
feat: migrate from setup.py/setuptools to pyproject.toml/hatch (#99)
* feat: migrate to pyproject.toml and hatch

* migrate from setup.py/setuptools to pyproject.toml/hatch.

* add optional dependencies

* fix license and author

* update maintainer name

---------

Co-authored-by: Muhammad Faraz  Maqsood <faraz.maqsood@192.168.10.35>
Co-authored-by: Muhammad Labeeb <mlabeeb03@gmail.com>
2025-03-02 14:14:48 +05:00
Muhammad Labeeb
4386c85adf docs: readme update
- Explicitly state that in order to use SSO with existing LMS admin users, the newly created superuser in discovery must have the same username and email as the LMS user.
2025-01-22 14:44:52 +05:00
Muhammad Faraz Maqsood
d34cc5dc75 docs: add changelog entry 2025-01-22 13:05:26 +05:00
Muhammad Faraz Maqsood
796c0497ec feat: add env variable to enable/disable programs
This commit includes:
- This aligns with the frontend, which now also utilizes this environment variable. To view the frontend related changes, click here: https://github.com/openedx/frontend-app-learner-dashboard/pull/506/files.
- Additionally, it automates the enabling of programs in the "programapiconfig" model on the LMS admin panel through init tasks.
- Update the documentation accordingly.
2025-01-22 13:05:26 +05:00
Syed Muhammad Dawoud Sheraz Ali
6ca50caa5a
build: re-add auto-add for PRs with a different target (#96) 2025-01-21 16:45:48 +05:00
Muhammad Faraz Maqsood
89b63c1d47 doc: add link to instructions to use event-bus 2025-01-09 13:09:40 +05:00
Muhammad Faraz Maqsood
20e381ed25 doc: add changelog entry 2025-01-09 13:09:40 +05:00
Muhammad Faraz Maqsood
604e7f3485 feat: add support to run event-bus in discovery
- add support to consume events from event bus in discovery. Explanation can be viewed here: https://github.com/openedx/event-bus-redis/blob/main/docs/tutor_installation.rst
- close #94
2025-01-09 13:09:40 +05:00
Régis Behmo
d343d1fbd3
feat: upgrade to sumac 2024-12-16 20:38:52 +01:00
Syed Muhammad Dawoud Sheraz Ali
214ae64628 chore: update changelog 2024-12-09 19:09:11 +05:00
Muhammad Faraz Maqsood
b05698b9a3 chore: add is_docker_rootless() in tutor-discovery
- move is_docker_rootless method from tutor to tutor-discovery
- move is_docker_rootless related tests from tutor to tutor-discovery and modify makefile according to it.
2024-11-29 22:31:39 +05:00
Muhammad Faraz Maqsood
403a5e297d feat: add Elasticsearch support in tutor-discovery (#89)
- As Tutor and Open edX have shifted to Meilisearch, and course-discovery still depends on Elasticsearch, running the Elasticsearch container with tutor-discovery will facilitate smoother operation for the course-discovery service.
- It's related PR from tutor: overhangio/tutor#1141.

Co-authored-by: Muhammad Faraz  Maqsood <faraz.maqsood@192.168.10.123>
2024-11-29 22:31:39 +05:00
Muhammad Faraz Maqsood
b89af42406 v19.0.0
upgrade to sumac
2024-11-29 22:31:39 +05:00
Régis Behmo
468662c75c Merge branch 'release' 2024-11-27 18:35:01 +01:00
Syed Muhammad Dawoud Sheraz Ali
1d57879cc8 feat!: Rename branches master->release, nightly->main 2024-11-27 22:33:41 +05:00
Overhang.IO
1807cd8abd Merge remote-tracking branch 'origin/master' into nightly 2024-11-14 16:21:13 +00:00
Muhammad Faraz Maqsood
9090614180
chore: remove Python 3.8 references (#91)
- drop support for python 3.8 and set Python 3.9 as the minimum supported python version.

Co-authored-by: Muhammad Faraz  Maqsood <faraz.maqsood@192.168.10.36>
2024-11-14 21:09:52 +05:00
Hina Khadim
1cc9c3f40c
feat: ubuntu upgrade in dockerfile (#84)
* feat: ubuntu upgrade  in  dockerfile

* fix: set app id to 1000

* fix: remove uwsgi

* fix: remove empty lines

* fix: update  changelog
2024-10-23 16:43:16 +05:00
Régis Behmo
7618ec9f03 docs: *.local.edly.io -> *.local.openedx.io
The default URL to run a local platform switched from local.edly.io to
local.openedx.io. This changes makes it clearer for everyone that Tutor
is to run Open edX.

See: https://github.com/overhangio/tutor/issues/1120
2024-10-17 08:36:40 +02:00
33 changed files with 427 additions and 117 deletions

View File

@ -4,6 +4,9 @@ on:
issues:
types:
- opened
pull_request_target:
types:
- opened
jobs:
# https://github.com/actions/add-to-project

View File

@ -2,7 +2,7 @@ name: Sync with private repo
on:
push:
branches: [ master, main, nightly ]
branches: [ release, main ]
jobs:
sync:

View File

@ -2,7 +2,9 @@ name: Run tests
on:
pull_request:
branches: [master]
branches: [release, main]
push:
branches: [release, main]
jobs:
tests:
@ -16,8 +18,6 @@ jobs:
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
- name: Upgrade pip
run: python -m pip install --upgrade pip setuptools
- name: Install dependencies
run: |
pip install .[dev]

22
.hatch_build.py Normal file
View File

@ -0,0 +1,22 @@
# https://hatch.pypa.io/latest/how-to/config/dynamic-metadata/
import os
import typing as t
from hatchling.metadata.plugin.interface import MetadataHookInterface
HERE = os.path.dirname(__file__)
class MetaDataHook(MetadataHookInterface):
def update(self, metadata: dict[str, t.Any]) -> None:
about = load_about()
metadata["version"] = about["__version__"]
def load_about() -> dict[str, str]:
about: dict[str, str] = {}
with open(
os.path.join(HERE, "tutordiscovery", "__about__.py"), "rt", encoding="utf-8"
) as f:
exec(f.read(), about)
return about

View File

@ -19,6 +19,41 @@ instructions, because git commits are used to generate release notes:
<!-- scriv-insert-here -->
<a id='changelog-20.0.0'></a>
## v20.0.0 (2025-06-05)
- [Bugfix] Add support to consume events from event bus in discovery. Explanation can be viewed here: https://github.com/openedx/event-bus-redis/blob/main/docs/tutor_installation.rst. (by @Faraz32123)
- [Feature] Introduced a new environment variable to enable or disable programs. (by @Faraz32123)
- This aligns with the frontend, which now also utilizes this environment variable. To view the frontend related changes, click here: https://github.com/openedx/frontend-app-learner-dashboard/pull/506/files.
- Additionally, it automates the enabling of programs in the "programapiconfig" model on the LMS admin panel through init tasks.
- [Improvement] Migrate packaging from setup.py/setuptools to pyproject.toml/hatch. (by @Faraz32123)
- For more details view tutor core PR: https://github.com/overhangio/tutor/pull/1163
- [Improvement] Add hatch_build.py in sdist target to fix the installation issues (by @dawoudsheraz)
- [Improvement] Replace site-configuration script with create_or_update_site_configuration management command in the init task. (by @Danyal-Faheem)
- 💥[Feature] Upgrade to Teak. (by @mlabeeb03)
<a id='changelog-19.0.0'></a>
## v19.0.0 (2024-10-23)
- 💥[Feature] Upgrade to Sumac. (by @Faraz32123)
- [Feature] Add Elasticsearch support in tutor-discovery. As Tutor and Open edX have shifted to Meilisearch, and course-discovery still depends on Elasticsearch, running the Elasticsearch container with tutor-discovery will facilitate smoother operation for the course-discovery service. (by @Faraz32123)
- Please see related tutor core PR for context https://github.com/overhangio/tutor/pull/1141
- 💥 [Deprecation] Drop support for python 3.8 and set Python 3.9 as the minimum supported python version. (by @Faraz32123)
- 💥[Improvement] Rename Tutor's two branches (by @DawoudSheraz):
* Rename **master** to **release**, as this branch runs the latest official Open edX release tag.
* Rename **nightly** to **main**, as this branch runs the Open edX master branches, which are the basis for the next Open edX release.
- [Improvement] Move is_docker_rootless method related to elasticsearch from tutor core to tutor-discovery. (by @Faraz32123)
- 💥[Feature] Update Course Discovery Image to use Ubuntu 22.04 as base OS. (by @hinakhadim)
- [Bugfix] Fix catalog_service_user permissions and 403 while fetching pathways (by @dyudyunov)
- [BugFix] Fix images(media) persistance issue by mounting media directory in volumes through patches. (by @Faraz32123)
- [Bugfix] Fix legacy warnings during Docker build. (by @regisb)
<a id='changelog-18.0.0'></a>
## v18.0.0 (2024-05-14)

View File

@ -1,2 +0,0 @@
recursive-include tutordiscovery/patches *
recursive-include tutordiscovery/templates *

View File

@ -1,25 +1,33 @@
.DEFAULT_GOAL := help
.PHONY: docs
SRC_DIRS = ./tutordiscovery
BLACK_OPTS = --exclude templates ${SRC_DIRS}
SRC_DIRS = ./tutordiscovery ./tests
# Warning: These checks are run on every PR.
test: test-lint test-types test-format # Run some static checks.
test: test-lint test-types test-format test-unit test-pythonpackage # Run some static checks.
test-format: ## Run code formatting tests.
black --check --diff $(BLACK_OPTS)
ruff format --check --diff ${SRC_DIRS}
test-lint: ## Run code linting tests
pylint --errors-only --enable=unused-import,unused-argument --ignore=templates --ignore=docs/_ext ${SRC_DIRS}
ruff check ${SRC_DIRS}
test-types: ## Run type checks.
mypy --exclude=templates --ignore-missing-imports --implicit-reexport --strict ${SRC_DIRS}
format: ## Format code automatically.
black $(BLACK_OPTS)
test-unit: ## Run unit tests
python -m unittest discover tests
isort: ## Sort imports. This target is not mandatory because the output may be incompatible with black formatting. Provided for convenience purposes.
isort --skip=templates ${SRC_DIRS}
build-pythonpackage: ## Build the "tutor-discovery" python package for upload to pypi
python -m build --sdist
test-pythonpackage: build-pythonpackage ## Test that package can be uploaded to pypi
twine check dist/tutor_discovery-$(shell make version).tar.gz
format: ## Format code automatically.
ruff format ${SRC_DIRS}
fix-lint: ## Fix lint errors automatically
ruff check --fix ${SRC_DIRS}
changelog-entry: ## Create a new changelog entry.
scriv create
@ -27,6 +35,9 @@ changelog-entry: ## Create a new changelog entry.
changelog: ## Collect changelog entries in the CHANGELOG.md file.
scriv collect
version: ## Print the current tutor-discovery version
@python -c 'import io, os; about = {}; exec(io.open(os.path.join("tutordiscovery", "__about__.py"), "rt", encoding="utf-8").read(), about); print(about["__version__"])'
ESCAPE = 
help: ## Print this help.
@grep -E '^([a-zA-Z_-]+:.*?## .*|######* .+)$$' Makefile \

View File

@ -38,7 +38,7 @@ Operations
Creating a user
~~~~~~~~~~~~~~~
The discovery user interface will be available at http://discovery.local.edly.io for a local test instance,
The discovery user interface will be available at http://discovery.local.openedx.io for a local test instance,
and at ``DISCOVERY_HOST`` (by default: http(s)://discovery.<your lms host>) in production. To run
commands from the UI, a user must be created:
@ -46,11 +46,15 @@ commands from the UI, a user must be created:
tutor local run discovery ./manage.py createsuperuser
Then, you must log in with this user at http://discovery.local.edly.io/admin.
Then, you must log in with this user at http://discovery.local.openedx.io/admin.
Alternatively, you can log in with oauth2 using a pre-existing user created on the LMS/CMS by accessing
http(s)://discovery.<your lms host>/login. To do so, the proper domain names must exist and point to
the production server.
Using SSO with LMS
~~~~~~~~~~~~~~~~~~
If you want to log in using Single Sign-On (SSO) with the LMS, ensure that the superuser you created
above in discovery has the same username and email as an existing admin user in the LMS/CMS. You can
then access the discovery interface via `http(s)://discovery.<your lms host>/login`. Make sure that
the proper domain names are configured and point to the production server.
Index configuration
~~~~~~~~~~~~~~~~~~~
@ -108,32 +112,43 @@ extra argument to the command. i.e. ``--domain``. While running tutor in product
.. code-block:: bash
tutor local run lms ./manage.py lms cache_programs --domain="local.edly.io"
tutor local run lms ./manage.py lms cache_programs --domain="local.openedx.io"
While running tutor in development mode:
.. code-block:: bash
tutor dev run lms ./manage.py lms cache_programs --domain="local.edly.io:8000"
tutor dev run lms ./manage.py lms cache_programs --domain="local.openedx.io:8000"
This last step should be performed every time you create new or make changes to existing programs.
Show Programs Tab
~~~~~~~~~~~~~~~~~
To make the ``Programs`` tab work in the LMS dashboard, users will need to manually create an entry
in the ``Programs api config`` model in the LMS Admin Panel. Go to http://local.edly.io/admin/programs/programsapiconfig/.
Add ``Marketing path`` equal to ``/programs`` and enable it. Then Programs tab will be shown on the LMS
where users can view their registered programs. It will show like in the below picture.
By default, the **Programs** tab is available in the LMS dashboard. Users can enable or disable this tab as needed.
To Disable Programs, run the following command:
.. code-block:: bash
tutor config save --set ENABLE_PROGRAMS=False
To Enable Programs, run the following command:
.. code-block:: bash
tutor config save --set ENABLE_PROGRAMS=True
Only programs in which learners are registered will appear on this page. If a learner is enrolled in any course that is part of a program, that program will be displayed here.
.. image:: https://github.com/overhangio/tutor-discovery/assets/122095701/e0224011-adc0-41e4-a104-af4cb0c24b82
:alt: Programs Tab on LMS dashboard
In the above image, the user can see explore programs button which is pointing to ``http://localhost:8080/programs`` by default.
This link does not exist. So, users can change this link to their custom-built marketing site URL to show all programs there.
This can be done by modifying the ``Site Configurations`` model in the LMS Admin Panel. Go to
http://local.edly.io/admin/site_configuration/siteconfiguration/. Open the respective LMS site configuration and add the below
dictionary in ``site values`` field like the below image:
In the image above, the **Explore Programs** button points to http://localhost:8080/programs by default. This link does not exist, so users can change it to their custom-built marketing site URL to display all programs.
To Modify the Link:
1. Go to the **Site Configurations** model in the LMS Admin Panel: `http://local.openedx.io/admin/site_configuration/siteconfiguration/`
2. Open the respective LMS site configuration.
3. Add the following dictionary in the **site values** field like in the below image:
.. code-block:: python
@ -144,6 +159,11 @@ dictionary in ``site values`` field like the below image:
.. image:: https://github.com/overhangio/tutor-discovery/assets/122095701/2d588ea9-a830-40b6-9845-8fab56d7cb5a
:alt: Add Custom Site for Explore Programs
By following above instructions, this link (https://custom-marketing-site-here.com) will be replaced by http://localhost:8080. Additionally, users can also replace "/programs" by following these below steps:
1. Go to: `http://local.openedx.io/admin/programs/programsapiconfig/`
2. Add **Marketing path** equal to "/programs" or your desired marketing site path and enable it.
Install extra requirements
~~~~~~~~~~~~~~~~~~~~~~~~~~
@ -169,7 +189,7 @@ in the development container:
tutor dev start --mount /path/to/course-discovery/ discovery
You can then access the development server at http://discovery.local.edly.io:8381. Feel free to add breakpoints
You can then access the development server at http://discovery.local.openedx.io:8381. Feel free to add breakpoints
(``import pdb; pdb.set_trace()``) anywhere in your source code to debug your application.
Once a local repository is mounted in the image, you will have to install nodejs dependencies and collect static assets:
@ -201,10 +221,20 @@ This entry may be present if you named your server with the LMS hostname.
.. _DigitalOcean: https://digitalocean.com/
Using event-bus with tutor-discovery
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Although tutor-discovery does not start event bus consumption by default, it supports running it. To consume events from event bus with tutor-discovery, follow these `instructions`_ provided by `event-bus-redis`_.
.. _instructions: https://github.com/openedx/event-bus-redis/blob/main/docs/tutor_installation.rst
.. _event-bus-redis: https://github.com/openedx/event-bus-redis
Troubleshooting
---------------
This Tutor plugin is maintained by Muhammad Faraz Maqsood from `Edly`_.
This Tutor plugin is maintained by Muhammad Labeeb from `Edly`_.
Community support is available from the official `Open edX forum`_.
Do you need help with this plugin? See the `troubleshooting`_
section from the Tutor documentation.
@ -218,4 +248,4 @@ License
This work is licensed under the terms of the `GNU Affero General Public License (AGPL)`_.
.. _GNU Affero General Public License (AGPL): https://github.com/overhangio/tutor/blob/master/LICENSE.txt
.. _GNU Affero General Public License (AGPL): https://github.com/overhangio/tutor/blob/release/LICENSE.txt

View File

@ -1 +0,0 @@
- [Bugfix] Fix legacy warnings during Docker build. (by @regisb)

View File

@ -1 +0,0 @@
- [BugFix] Fix images(media) persistance issue by mounting media directory in volumes through patches. (by @Faraz32123)

View File

@ -1 +0,0 @@
- [Bugfix] Fix catalog_service_user permissions and 403 while fetching pathways (by @dyudyunov)

View File

@ -1 +0,0 @@
- 💥 [Deprecation] Drop support for python 3.8 and set Python 3.9 as the minimum supported python version. (by @Faraz32123)

View File

@ -0,0 +1,2 @@
- [Improvement] Migrate from pylint and black to ruff. (by @mlabeeb03)
- [Improvement] Test python package distribution build when running make test. (by @mlabeeb03)

View File

@ -1,2 +1,84 @@
# https://packaging.python.org/en/latest/tutorials/packaging-projects/
# https://hatch.pypa.io/latest/config/build/
[project]
name = "tutor-discovery"
license = { text = "AGPL-3.0-only" }
authors = [
{name = "Edly"},
{email = "hello@edly.io"},
]
maintainers = [
{name = "Muhammad Labeeb"},
{email = "muhammad.labeeb@arbisoft.com"},
]
description="A Tutor plugin for course discovery, the Open edX service for providing access to consolidated course and program metadata"
readme = {file = "README.rst", content-type = "text/x-rst"}
requires-python = ">= 3.9"
classifiers = [
"Development Status :: 5 - Production/Stable",
"Intended Audience :: Developers",
"License :: OSI Approved :: GNU Affero General Public License v3",
"Operating System :: OS Independent",
"Programming Language :: Python",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
]
dependencies = [
"tutor>=20.0.0,<21.0.0",
]
# these fields will be set by hatch_build.py
dynamic = ["version"]
[project.optional-dependencies]
dev = [
"tutor[dev]>=20.0.0,<21.0.0",
"ruff",
]
# https://packaging.python.org/en/latest/specifications/well-known-project-urls/#well-known-labels
[project.urls]
Homepage = "https://docs.tutor.edly.io/"
Documentation = "https://docs.tutor.edly.io/"
Source = "https://github.com/overhangio/tutor-discovery"
Issues = "https://github.com/overhangio/tutor-discovery/issues"
Changelog = "https://github.com/overhangio/tutor-discovery/blob/release/CHANGELOG.md"
Community = "https://discuss.openedx.org/tag/tutor"
# hatch-specific configuration
[tool.hatch.metadata.hooks.custom]
path = ".hatch_build.py"
[build-system]
requires = ["setuptools", "wheel"]
requires = ["hatchling"]
build-backend = "hatchling.build"
[tool.hatch.build.targets.sdist]
# Disable strict naming, otherwise twine is not able to detect name/version
strict-naming = false
include = [ "/tutordiscovery", ".hatch_build.py"]
exclude = ["tests*"]
[tool.hatch.build.targets.wheel]
packages = ["tutordiscovery"]
[tool.ruff]
exclude = ["templates", "docs/_ext"]
[tool.ruff.lint]
# E: pycodestyle errors
# I: isort
# N: pep8-naming
select = ["E", "I", "N"]
# F401: unused-import
# F841: unused-variable
# W292: missing-newline-at-end-of-file
extend-select = ["F401", "F841", "W292"]
[tool.ruff.format]
[project.entry-points."tutor.plugin.v1"]
discovery = "tutordiscovery.plugin"

View File

@ -1,52 +0,0 @@
import io
import os
from setuptools import setup, find_packages
here = os.path.abspath(os.path.dirname(__file__))
with io.open(os.path.join(here, "README.rst"), "rt", encoding="utf8") as f:
readme = f.read()
about = {}
with io.open(
os.path.join(here, "tutordiscovery", "__about__.py"), "rt", encoding="utf-8"
) as f:
exec(f.read(), about)
setup(
name="tutor-discovery",
version=about["__version__"],
url="https://docs.tutor.edly.io/",
project_urls={
"Documentation": "https://docs.tutor.edly.io/",
"Code": "https://github.com/overhangio/tutor-discovery",
"Issue tracker": "https://github.com/overhangio/tutor-discovery/issues",
"Community": "https://discuss.openedx.org",
},
license="AGPLv3",
author="Edly",
author_email="hello@edly.io",
maintainer="Edly",
maintainer_email="faraz.maqsood@arbisoft.com",
description="A Tutor plugin for course discovery, the Open edX service for providing access to consolidated course and program metadata",
long_description=readme,
long_description_content_type="text/x-rst",
packages=find_packages(exclude=["tests*"]),
include_package_data=True,
install_requires=["tutor>=18.0.0,<19.0.0"],
extras_require={"dev": "tutor[dev]>=18.0.0,<19.0.0"},
python_requires=">=3.9",
entry_points={"tutor.plugin.v1": ["discovery = tutordiscovery.plugin"]},
classifiers=[
"Development Status :: 5 - Production/Stable",
"Intended Audience :: Developers",
"License :: OSI Approved :: GNU Affero General Public License v3",
"Operating System :: OS Independent",
"Programming Language :: Python",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
],
)

0
tests/__init__.py Normal file
View File

26
tests/test_utils.py Normal file
View File

@ -0,0 +1,26 @@
import subprocess
import unittest
from unittest.mock import MagicMock, patch
from tutordiscovery import utils
class UtilsTests(unittest.TestCase):
@patch("subprocess.run")
def test_is_docker_rootless(self, mock_run: MagicMock) -> None:
# Mock rootless `docker info` output
utils.is_docker_rootless.cache_clear()
mock_run.return_value.stdout = "some prefix\n rootless foo bar".encode("utf-8")
self.assertTrue(utils.is_docker_rootless())
# Mock regular `docker info` output
utils.is_docker_rootless.cache_clear()
mock_run.return_value.stdout = "some prefix, regular docker".encode("utf-8")
self.assertFalse(utils.is_docker_rootless())
@patch("subprocess.run")
def test_is_docker_rootless_podman(self, mock_run: MagicMock) -> None:
"""Test the `is_docker_rootless` when podman is used or any other error with `docker info`""" # noqa: E501
utils.is_docker_rootless.cache_clear()
mock_run.side_effect = subprocess.CalledProcessError(1, "docker info")
self.assertFalse(utils.is_docker_rootless())

View File

@ -1 +1 @@
__version__ = "18.0.0"
__version__ = "20.0.0"

View File

@ -0,0 +1 @@
DISCOVERY_RUN_ELASTICSEARCH = {{ DISCOVERY_RUN_ELASTICSEARCH }}

View File

@ -15,8 +15,8 @@ spec:
app.kubernetes.io/name: discovery
spec:
securityContext:
runAsUser: 1000
runAsGroup: 1000
runAsUser: {{ APP_USER_ID }}
runAsGroup: {{ APP_USER_ID }}
containers:
- name: discovery
image: {{ DISCOVERY_DOCKER_IMAGE }}
@ -32,3 +32,54 @@ spec:
- name: settings
configMap:
name: discovery-settings
{% if DISCOVERY_RUN_ELASTICSEARCH %}
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: elasticsearch
labels:
app.kubernetes.io/name: elasticsearch
spec:
selector:
matchLabels:
app.kubernetes.io/name: elasticsearch
strategy:
type: Recreate
template:
metadata:
labels:
app.kubernetes.io/name: elasticsearch
spec:
securityContext:
runAsUser: {{ APP_USER_ID }}
runAsGroup: {{ APP_USER_ID }}
fsGroup: {{ APP_USER_ID }}
fsGroupChangePolicy: "OnRootMismatch"
containers:
- name: elasticsearch
image: {{ DISCOVERY_DOCKER_IMAGE_ELASTICSEARCH }}
env:
- name: cluster.name
value: "openedx"
- name: bootstrap.memory_lock
value: "true"
- name: discovery.type
value: "single-node"
- name: ES_JAVA_OPTS
value: "-Xms{{ DISCOVERY_ELASTICSEARCH_HEAP_SIZE }} -Xmx{{ DISCOVERY_ELASTICSEARCH_HEAP_SIZE }}"
- name: TAKE_FILE_OWNERSHIP
value: "1"
ports:
- containerPort: 9200
securityContext:
allowPrivilegeEscalation: false
volumeMounts:
- mountPath: /usr/share/elasticsearch/data
name: data
volumes:
- name: data
persistentVolumeClaim:
claimName: elasticsearch
{% endif %}

View File

@ -23,4 +23,3 @@ spec:
- name: settings
configMap:
name: discovery-settings

View File

@ -10,3 +10,19 @@ spec:
protocol: TCP
selector:
app.kubernetes.io/name: discovery
{% if DISCOVERY_RUN_ELASTICSEARCH %}
---
apiVersion: v1
kind: Service
metadata:
name: elasticsearch
labels:
app.kubernetes.io/name: elasticsearch
spec:
type: ClusterIP
ports:
- port: 9200
protocol: TCP
selector:
app.kubernetes.io/name: elasticsearch
{% endif %}

View File

@ -0,0 +1,16 @@
{% if DISCOVERY_RUN_ELASTICSEARCH %}
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: elasticsearch
labels:
app.kubernetes.io/component: volume
app.kubernetes.io/name: elasticsearch
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 2Gi
{% endif %}

View File

@ -15,3 +15,12 @@ discovery:
default:
aliases:
- "{{ DISCOVERY_HOST }}"
{% if DISCOVERY_RUN_ELASTICSEARCH and is_docker_rootless() %}
elasticsearch:
ulimits:
memlock:
# Fixes error setting rlimits for ready process in rootless docker
soft: 0 # zero means "unset" in the memlock context
hard: 0
{% endif %}

View File

@ -0,0 +1 @@
{% if DISCOVERY_RUN_ELASTICSEARCH %}setowner {{ APP_USER_ID }} /mounts/elasticsearch{% endif %}

View File

@ -0,0 +1 @@
{% if DISCOVERY_RUN_ELASTICSEARCH %}- ../../data/elasticsearch:/mounts/elasticsearch{% endif %}

View File

@ -6,7 +6,28 @@ discovery:
volumes:
- ../plugins/discovery/apps/settings/tutor:/openedx/discovery/course_discovery/settings/tutor:ro
- ../../data/discovery/media:/openedx/discovery/course_discovery/media
{% if DISCOVERY_RUN_ELASTICSEARCH %}- ../../data/elasticsearch:/mounts/elasticsearch{% endif %}
depends_on:
- lms
{% if RUN_MYSQL %}- mysql{% endif %}
{% if RUN_ELASTICSEARCH %}- elasticsearch{% endif %}
{% if DISCOVERY_RUN_ELASTICSEARCH %}- elasticsearch{% endif %}
{% if DISCOVERY_RUN_ELASTICSEARCH -%}
elasticsearch:
image: {{ DISCOVERY_DOCKER_IMAGE_ELASTICSEARCH }}
environment:
- cluster.name=openedx
- bootstrap.memory_lock=true
- discovery.type=single-node
- "ES_JAVA_OPTS=-Xms{{ DISCOVERY_ELASTICSEARCH_HEAP_SIZE }} -Xmx{{ DISCOVERY_ELASTICSEARCH_HEAP_SIZE }}"
ulimits:
memlock:
soft: -1
hard: -1
restart: unless-stopped
user: "{{ APP_USER_ID }}:{{ APP_USER_ID }}"
volumes:
- ../../data/elasticsearch:/usr/share/elasticsearch/data
depends_on:
- permissions
{%- endif %}

View File

@ -0,0 +1 @@
MFE_CONFIG["ENABLE_PROGRAMS"] = {{ ENABLE_PROGRAMS }}

View File

@ -9,8 +9,9 @@ from tutor import hooks as tutor_hooks
from tutor.__about__ import __version_suffix__
from .__about__ import __version__
from .utils import is_docker_rootless
# Handle version suffix in nightly mode, just like tutor core
# Handle version suffix in main mode, just like tutor core
if __version_suffix__:
__version__ += "-" + __version_suffix__
@ -21,7 +22,7 @@ APP_NAME = "discovery"
config: t.Dict[str, t.Dict[str, t.Any]] = {
"defaults": {
"VERSION": __version__,
"DOCKER_IMAGE": "{{ DOCKER_REGISTRY}}overhangio/openedx-discovery:{{ DISCOVERY_VERSION }}",
"DOCKER_IMAGE": "{{ DOCKER_REGISTRY}}overhangio/openedx-discovery:{{ DISCOVERY_VERSION }}", # noqa: E501
"HOST": "discovery.{{ LMS_HOST }}",
"INDEX_OVERRIDES": {},
"MYSQL_DATABASE": "discovery",
@ -36,6 +37,12 @@ config: t.Dict[str, t.Dict[str, t.Any]] = {
"EXTRA_PIP_REQUIREMENTS": [],
"REPOSITORY": "https://github.com/openedx/course-discovery.git",
"REPOSITORY_VERSION": "{{ OPENEDX_COMMON_VERSION }}",
"RUN_ELASTICSEARCH": True,
"DOCKER_IMAGE_ELASTICSEARCH": "docker.io/elasticsearch:7.17.13",
"ELASTICSEARCH_HOST": "elasticsearch",
"ELASTICSEARCH_PORT": 9200,
"ELASTICSEARCH_SCHEME": "http",
"ELASTICSEARCH_HEAP_SIZE": "1g",
},
"unique": {
"MYSQL_PASSWORD": "{{ 8|random_string }}",
@ -43,6 +50,9 @@ config: t.Dict[str, t.Dict[str, t.Any]] = {
"OAUTH2_SECRET": "{{ 8|random_string }}",
"OAUTH2_SECRET_SSO": "{{ 8|random_string }}",
},
"overrides": {
"ENABLE_PROGRAMS": True,
},
}
# Initialization tasks
@ -121,6 +131,10 @@ tutor_hooks.Filters.ENV_TEMPLATE_TARGETS.add_items(
("discovery/apps", "plugins"),
],
)
# Template variables
tutor_hooks.Filters.ENV_TEMPLATE_VARIABLES.add_item(
("is_docker_rootless", is_docker_rootless),
)
# Load patches from files
for path in glob(
os.path.join(

View File

@ -20,8 +20,14 @@ DATABASES = {
}
}
DISCOVERY_DOCKER_IMAGE_ELASTICSEARCH = "{{ DISCOVERY_DOCKER_IMAGE_ELASTICSEARCH }}"
DISCOVERY_ELASTICSEARCH_HOST = "{{ DISCOVERY_ELASTICSEARCH_HOST }}"
DISCOVERY_ELASTICSEARCH_PORT = "{{ DISCOVERY_ELASTICSEARCH_PORT }}"
DISCOVERY_ELASTICSEARCH_SCHEME = "{{ DISCOVERY_ELASTICSEARCH_SCHEME }}"
DISCOVERY_ELASTICSEARCH_HEAP_SIZE = "{{ DISCOVERY_ELASTICSEARCH_HEAP_SIZE }}"
ELASTICSEARCH_DSL['default'].update({
'hosts': "{{ ELASTICSEARCH_SCHEME }}://{{ ELASTICSEARCH_HOST }}:{{ ELASTICSEARCH_PORT }}/"
'hosts': "{{ DISCOVERY_ELASTICSEARCH_SCHEME }}://{{ DISCOVERY_ELASTICSEARCH_HOST }}:{{ DISCOVERY_ELASTICSEARCH_PORT }}/"
})
{% for name, index in DISCOVERY_INDEX_OVERRIDES.items() %}

View File

@ -1,6 +1,6 @@
# syntax=docker/dockerfile:1.4
###### Minimal image with base system requirements for most stages
FROM docker.io/ubuntu:20.04 AS minimal
FROM docker.io/ubuntu:22.04 AS minimal
ENV DEBIAN_FRONTEND=noninteractive
RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \
@ -8,10 +8,10 @@ RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \
apt update && \
apt install -y curl git-core gettext language-pack-en \
build-essential libcairo2 libffi-dev libmysqlclient-dev libxml2-dev libxslt-dev libjpeg-dev libssl-dev \
pkg-config libsqlite3-dev mime-support
pkg-config libsqlite3-dev media-types mailcap libbz2-dev liblzma-dev
ENV LC_ALL=en_US.UTF-8
ARG APP_USER_ID=1000
ARG APP_USER_ID={{ HOST_USER_ID }}
RUN if [ "$APP_USER_ID" = 0 ]; then echo "app user may not be root" && false; fi
RUN useradd --home-dir /openedx --create-home --shell /bin/bash --uid ${APP_USER_ID} app
USER ${APP_USER_ID}
@ -47,10 +47,10 @@ RUN --mount=type=cache,target=/openedx/.cache/pip,sharing=shared pip install \
# https://pypi.org/project/setuptools/
# https://pypi.org/project/pip/
# https://pypi.org/project/wheel/
setuptools==69.1.1 pip==24.0 wheel==0.43.0
setuptools==77.0.3 pip==25.0.1 wheel==0.45.1
# Install a recent version of nodejs
RUN pip install nodeenv==1.8.0
RUN pip install nodeenv==1.9.1
# nodejs version picked from https://github.com/openedx/course-discovery/blob/master/Dockerfile
RUN nodeenv /openedx/nodeenv --node=16.14.2 --prebuilt
ENV PATH=/openedx/nodeenv/bin:${PATH}
@ -73,7 +73,7 @@ RUN --mount=type=cache,target=/openedx/.cache/pip,sharing=shared pip install \
# Use redis as a django cache https://pypi.org/project/django-redis/
django-redis==5.4.0 \
# uwsgi server https://pypi.org/project/uWSGI/
uwsgi==2.0.24
uwsgi==2.0.28
{% if DISCOVERY_ATLAS_PULL %}
# Pull translations. Support the OEP-58 proposal behind a feature flag until it's fully implemented.
@ -91,13 +91,13 @@ RUN mkdir course_discovery/media
# Run production server
ENV DJANGO_SETTINGS_MODULE=course_discovery.settings.tutor.production
EXPOSE 8000
CMD uwsgi \
--static-map /static=/openedx/discovery/course_discovery/assets \
--static-map /media=/openedx/discovery/course_discovery/media \
--http 0.0.0.0:8000 \
--thunder-lock \
--single-interpreter \
--enable-threads \
--processes=2 \
--buffer-size=8192 \
--wsgi-file course_discovery/wsgi.py
CMD ["uwsgi", \
"--static-map", "/static=/openedx/discovery/course_discovery/assets", \
"--static-map", "/media=/openedx/discovery/course_discovery/media", \
"--http", "0.0.0.0:8000", \
"--thunder-lock", \
"--single-interpreter", \
"--enable-threads", \
"--processes=2", \
"--buffer-size=8192", \
"--wsgi-file", "course_discovery/wsgi.py"]

View File

@ -6,6 +6,11 @@
"from django.contrib.auth import get_user_model;\
get_user_model().objects.filter(username='lms_catalog_service_user').exclude(email='lms_catalog_service_user@openedx').update(email='lms_catalog_service_user@openedx')"
./manage.py lms shell -c \
"from openedx.core.djangoapps.programs.models import ProgramsApiConfig;\
ProgramsApiConfig.current().enabled or \
ProgramsApiConfig.objects.create(marketing_path='/programs', enabled=True)"
./manage.py lms manage_user discovery discovery@openedx --staff --superuser --unusable-password
./manage.py lms manage_user lms_catalog_service_user lms_catalog_service_user@openedx --staff --unusable-password
@ -57,5 +62,5 @@
# configuration -- which means that it takes different values for different
# sites. This is important because the programs and courses returned for each
# site will differ.
site-configuration set -d {{ LMS_HOST }} COURSE_CATALOG_API_URL {% if ENABLE_HTTPS %}https{% else %}http{% endif %}://{{ DISCOVERY_HOST }}/api/v1
site-configuration set -d {{ LMS_HOST }}:8000 COURSE_CATALOG_API_URL http://{{ DISCOVERY_HOST }}:8381/api/v1
./manage.py lms create_or_update_site_configuration {{ LMS_HOST }} --configuration '{"COURSE_CATALOG_API_URL": "{% if ENABLE_HTTPS %}https{% else %}http{% endif %}://{{ DISCOVERY_HOST }}/api/v1"}' --enabled
./manage.py lms create_or_update_site_configuration {{ LMS_HOST }}:8000 --configuration '{"COURSE_CATALOG_API_URL": "http://{{ DISCOVERY_HOST }}:8381/api/v1"}' --enabled

16
tutordiscovery/utils.py Normal file
View File

@ -0,0 +1,16 @@
import subprocess
from functools import lru_cache
@lru_cache(maxsize=None)
def is_docker_rootless() -> bool:
"""
A helper function to determine if Docker is running in rootless mode.
- https://docs.docker.com/engine/security/rootless/
"""
try:
results = subprocess.run(["docker", "info"], capture_output=True, check=True)
return "rootless" in results.stdout.decode()
except subprocess.CalledProcessError:
return False