-
Notifications
You must be signed in to change notification settings - Fork 3.9k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Move user retirement scripts code from the tubular repo (#34063)
* refactor: Migragte user retirement scripts code from the tubular repo
- Loading branch information
Showing
48 changed files
with
7,808 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
name: units-test-scripts-user-retirement | ||
|
||
on: | ||
pull_request: | ||
push: | ||
branches: | ||
- master | ||
|
||
jobs: | ||
test: | ||
runs-on: ubuntu-latest | ||
|
||
strategy: | ||
matrix: | ||
python-version: [ '3.8' ] | ||
|
||
steps: | ||
- name: Checkout code | ||
uses: actions/checkout@v4 | ||
|
||
- name: Set up Python | ||
uses: actions/setup-python@v4 | ||
with: | ||
python-version: ${{ matrix.python-version }} | ||
|
||
- name: Install dependencies | ||
run: | | ||
python -m pip install --upgrade pip | ||
pip install -r scripts/user_retirement/requirements/testing.txt | ||
- name: Run pytest | ||
run: | | ||
pytest scripts/user_retirement |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,100 @@ | ||
User Retirement Scripts | ||
======================= | ||
|
||
`This <https://github.com/openedx/edx-platform/tree/master/scripts/user_retirement>`_ directory contains python scripts which are migrated from the `tubular <https://github.com/openedx/tubular/tree/master/scripts>`_ respository. | ||
These scripts are intended to drive the user retirement workflow which involves handling the deactivation or removal of user accounts as part of the platform's management process. | ||
|
||
These scripts could be called from any automation/CD framework. | ||
|
||
How to run the scripts | ||
====================== | ||
|
||
Download the Scripts | ||
-------------------- | ||
|
||
To download the scripts, you can perform a partial clone of the edx-platform repository to obtain only the required scripts. The following steps demonstrate how to achieve this. Alternatively, you may choose other utilities or libraries for the partial clone. | ||
|
||
.. code-block:: bash | ||
[email protected]:openedx/edx-platform.git | ||
branch=master | ||
directory=scripts/user_retirement | ||
git clone --branch $branch --single-branch --depth=1 --filter=tree:0 $repo_url | ||
cd edx-platform | ||
git sparse-checkout init --cone | ||
git sparse-checkout set $directory | ||
Create Python Virtual Environment | ||
--------------------------------- | ||
|
||
Create a Python virtual environment using Python 3.8: | ||
|
||
.. code-block:: bash | ||
python3.8 -m venv ../venv | ||
source ../venv/bin/activate | ||
Install Pip Packages | ||
-------------------- | ||
|
||
Install the required pip packages using the provided requirements file: | ||
|
||
.. code-block:: bash | ||
pip install -r scripts/user_retirement/requirements/base.txt | ||
In-depth Documentation and Configuration Steps | ||
---------------------------------------------- | ||
|
||
For in-depth documentation and essential configurations follow these docs | ||
|
||
`Documentation <https://edx.readthedocs.io/projects/edx-installing-configuring-and-running/en/latest/configuration/user_retire/index.html>`_ | ||
|
||
`Configuration Docs <https://edx.readthedocs.io/projects/edx-installing-configuring-and-running/en/latest/configuration/user_retire/driver_setup.html>`_ | ||
|
||
|
||
Execute Script | ||
-------------- | ||
|
||
Execute the following shell command to establish entry points for the scripts | ||
|
||
.. code-block:: bash | ||
chmod +x scripts/user_retirement/entry_points.sh | ||
source scripts/user_retirement/entry_points.sh | ||
To retire a specific learner, you can use the provided example script: | ||
|
||
.. code-block:: bash | ||
retire_one_learner.py \ | ||
--config_file=src/config.yml \ | ||
--username=user1 | ||
Make sure to replace ``src/config.yml`` with the actual path to your configuration file and ``user1`` with the actual username. | ||
|
||
You can also execute Python scripts directly using the file path: | ||
|
||
.. code-block:: bash | ||
python scripts/user_retirement/retire_one_learner.py \ | ||
--config_file=src/config.yml \ | ||
--username=user1 | ||
Feel free to customize these steps according to your specific environment and requirements. | ||
|
||
Run Test Cases | ||
============== | ||
|
||
Before running test cases, install the testing requirements: | ||
|
||
.. code-block:: bash | ||
pip install -r scripts/user_retirement/requirements/testing.txt | ||
Run the test cases using pytest: | ||
|
||
.. code-block:: bash | ||
pytest scripts/user_retirement |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
#!/usr/bin/env bash | ||
alias get_learners_to_retire.py='python scripts/user_retirement/get_learners_to_retire.py' | ||
alias replace_usernames.py='python scripts/user_retirement/replace_usernames.py' | ||
alias retire_one_learner.py='python scripts/user_retirement/retire_one_learner.py' | ||
alias retirement_archive_and_cleanup.py='python scripts/user_retirement/retirement_archive_and_cleanup.py' | ||
alias retirement_bulk_status_update.py='python scripts/user_retirement/retirement_bulk_status_update.py' | ||
alias retirement_partner_report.py='python scripts/user_retirement/retirement_partner_report.py' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,105 @@ | ||
#! /usr/bin/env python3 | ||
|
||
""" | ||
Command-line script to retrieve list of learners that have requested to be retired. | ||
The script calls the appropriate LMS endpoint to get this list of learners. | ||
""" | ||
|
||
import io | ||
import logging | ||
import sys | ||
from os import path | ||
|
||
import click | ||
import yaml | ||
|
||
# Add top-level project path to sys.path before importing scripts code | ||
sys.path.append(path.abspath(path.join(path.dirname(__file__), '../..'))) | ||
|
||
from scripts.user_retirement.utils.edx_api import LmsApi | ||
from scripts.user_retirement.utils.jenkins import export_learner_job_properties | ||
|
||
logging.basicConfig(stream=sys.stdout, level=logging.DEBUG) | ||
LOG = logging.getLogger(__name__) | ||
|
||
|
||
@click.command("get_learners_to_retire") | ||
@click.option( | ||
'--config_file', | ||
help='File in which YAML config exists that overrides all other params.' | ||
) | ||
@click.option( | ||
'--cool_off_days', | ||
help='Number of days a learner should be in the retirement queue before being actually retired.', | ||
default=7 | ||
) | ||
@click.option( | ||
'--output_dir', | ||
help="Directory in which to write the Jenkins properties files.", | ||
default='./jenkins_props' | ||
) | ||
@click.option( | ||
'--user_count_error_threshold', | ||
help="If more users than this number are returned we will error out instead of retiring. This is a failsafe" | ||
"against attacks that somehow manage to add users to the retirement queue.", | ||
default=300 | ||
) | ||
@click.option( | ||
'--max_user_batch_size', | ||
help="This setting will only get at most X number of users. If this number is lower than the user_count_error_threshold" | ||
"setting then it will not error.", | ||
default=200 | ||
) | ||
def get_learners_to_retire(config_file, | ||
cool_off_days, | ||
output_dir, | ||
user_count_error_threshold, | ||
max_user_batch_size): | ||
""" | ||
Retrieves a JWT token as the retirement service user, then calls the LMS | ||
endpoint to retrieve the list of learners awaiting retirement. | ||
""" | ||
if not config_file: | ||
click.echo('A config file is required.') | ||
sys.exit(-1) | ||
|
||
with io.open(config_file, 'r') as config: | ||
config_yaml = yaml.safe_load(config) | ||
|
||
user_count_error_threshold = int(user_count_error_threshold) | ||
cool_off_days = int(cool_off_days) | ||
|
||
client_id = config_yaml['client_id'] | ||
client_secret = config_yaml['client_secret'] | ||
lms_base_url = config_yaml['base_urls']['lms'] | ||
retirement_pipeline = config_yaml['retirement_pipeline'] | ||
end_states = [state[1] for state in retirement_pipeline] | ||
states_to_request = ['PENDING'] + end_states | ||
|
||
api = LmsApi(lms_base_url, lms_base_url, client_id, client_secret) | ||
|
||
# Retrieve the learners to retire and export them to separate Jenkins property files. | ||
learners_to_retire = api.learners_to_retire(states_to_request, cool_off_days, max_user_batch_size) | ||
if max_user_batch_size: | ||
learners_to_retire = learners_to_retire[:max_user_batch_size] | ||
learners_to_retire_cnt = len(learners_to_retire) | ||
|
||
if learners_to_retire_cnt > user_count_error_threshold: | ||
click.echo( | ||
'Too many learners to retire! Expected {} or fewer, got {}!'.format( | ||
user_count_error_threshold, | ||
learners_to_retire_cnt | ||
) | ||
) | ||
sys.exit(-1) | ||
|
||
export_learner_job_properties( | ||
learners_to_retire, | ||
output_dir | ||
) | ||
|
||
|
||
if __name__ == "__main__": | ||
# pylint: disable=unexpected-keyword-arg, no-value-for-parameter | ||
# If using env vars to provide params, prefix them with "RETIREMENT_", e.g. RETIREMENT_CLIENT_ID | ||
get_learners_to_retire(auto_envvar_prefix='RETIREMENT') |
Empty file.
Oops, something went wrong.