Commit 6180f3a0 authored by Abhishek's avatar Abhishek
Browse files

DockerFile, docker-compose added

parent 3e15436c
FROM debian:bookworm
# Install Apache, PHP, and other required extensions
RUN apt-get update && apt-get install -y \
apache2 \
php \
php-cli \
php-gd \
php-mysql \
php-intl \
php-curl \
php-xml \
php-xmlrpc \
php-soap \
php-zip \
php-mbstring \
unzip \
git \
&& rm -rf /var/lib/apt/lists/*
# Add custom PHP settings for Moodle
# We copy it to both the apache2 and cli config directories
COPY moodle.ini /etc/php/8.2/apache2/conf.d/99-moodle.ini
COPY moodle.ini /etc/php/8.2/cli/conf.d/99-moodle.ini
# Download and unpack Moodle
ARG MOODLE_VERSION=403
RUN rm -rf /var/www/html
RUN git clone --depth 1 --branch MOODLE_${MOODLE_VERSION}_STABLE https://github.com/moodle/moodle.git /var/www/html/
# Create the moodledata directory
RUN mkdir /var/www/moodledata && chown -R www-data:www-data /var/www/moodledata
# Set correct permissions
WORKDIR /var/www/html
RUN chown -R www-data:www-data /var/www/html
# Copy a startup script
COPY --chown=root:root docker-entrypoint.sh /
ENTRYPOINT ["/docker-entrypoint.sh"]
CMD ["apache2ctl", "-D", "FOREGROUND"]
version: '3.8'
services:
db:
image: mariadb:10.6
container_name: moodle-db-official
command:
- --character-set-server=utf8mb4
- --collation-server=utf8mb4_unicode_ci
volumes:
- db_data:/var/lib/mysql
environment:
- MARIADB_ROOT_PASSWORD=verysecretpassword
- MARIADB_DATABASE=moodle
- MARIADB_USER=moodleuser
- MARIADB_PASSWORD=moodlepassword
restart: unless-stopped
web:
build: . # This tells it to use the Dockerfile in this directory
container_name: moodle-web-official
volumes:
# Mount our local plugin for easy installation
- ./dta.zip:/tmp/dta.zip
# Mount the moodledata directory as a named volume
- moodledata:/var/www/moodledata
ports:
- "8080:80"
depends_on:
- db
restart: unless-stopped
backend:
container_name: backend
image: hftstuttgart/cota-backend:latest
user: "${EUSER}:${EGID}"
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- '/tmp/dta-tests:/tmp/dta-tests'
restart: unless-stopped
# This service can be reached by the 'web' container at http://backend:[port]
cache:
container_name: cachedta
image: nginx:alpine
ports:
- '82:8080'
volumes:
- './conf:/etc/nginx/conf.d' # Nginx configs go here
depends_on:
- backend
restart: unless-stopped
volumes:
db_data:
moodledata:
#!/bin/bash
set -e
# Apache gets grumpy about PID files pre-existing
rm -f /var/run/apache2/apache2.pid
exec "$@"
File added
max_input_vars = 5000
worker_processes 5;
worker_rlimit_nofile 8192;
events {
worker_connections 4096;
}
http {
default_type application/octet-stream;
sendfile on;
tcp_nopush on;
server_names_hash_bucket_size 128;
proxy_cache_path /opt/cache levels=1:2 keys_zone=dta_cache:10m max_size=10g inactive=60m use_temp_path=off;
server {
listen 8080;
location /v1/task {
proxy_cache dta_cache;
proxy_cache_background_update on;
proxy_cache_lock on;
proxy_cache_key "$scheme$request_uri";
proxy_cache_valid 200 302 1d;
proxy_cache_methods GET HEAD POST;
proxy_pass http://backendcomposedta:8080/v1/task;
}
location /v1/unittest {
proxy_pass http://backendcomposedta:8080/v1/unittest;
}
}
}
dta::https://transfer.hft-stuttgart.de/gitlab/cota/examples/cota-test-course-jdk.git::none::none::/topic02/exercise04::cota-runner-jdk21-junit5
\ No newline at end of file
#!/bin/bash
set -e
# ==============================================================================
# DEFINITIVE Moodle CI/CD AUTOMATION SCRIPT V10 (ROBUST ERROR CHECKING)
# ==============================================================================
# --- Configuration (UPDATE THESE!) ---
MOODLE_URL="http://localhost:8080"
MOODLE_ADMIN_USER="user" # <-- IMPORTANT: CHANGE THIS
MOODLE_ADMIN_PASS="QAYwsx@12345" # <-- IMPORTANT: CHANGE THIS
DTA_CONFIG_FILE="teacher-dta-dir-test-runner-jdk21.txt"
# --- Static Configuration ---
MOODLE_CONTAINER="moodle-web-official"
MOODLE_SYSTEM_USER="www-data"
MOODLE_ROOT_IN_CONTAINER="/var/www/html"
PLUGIN_ZIP_NAME="dta.zip"
CUSTOM_SERVICE_NAME="dta_automation_service"
DB_CONTAINER_NAME="moodle-db-official"
DB_ROOT_PASSWORD="verysecretpassword"
DB_NAME="moodle"
# ==============================================================================
# SCRIPT EXECUTION
# ==============================================================================
echo "🚀 Starting Definitive Moodle Automation Script..."
# --- Pre-flight Checks & Plugin Install (Step 1) ---
if ! docker ps --format '{{.Names}}' | grep -qw "$MOODLE_CONTAINER"; then echo "❌ Moodle container not running." && exit 1; fi
if [ ! -f "$PLUGIN_ZIP_NAME" ]; then echo "❌ Plugin ZIP file not found." && exit 1; fi
if [ ! -f "$DTA_CONFIG_FILE" ]; then echo "❌ DTA config file not found." && exit 1; fi
echo "✅ Pre-flight checks passed."
echo "📦 Step 1: Installing custom plugin..."
docker exec -u "$MOODLE_SYSTEM_USER" "$MOODLE_CONTAINER" bash -c "set -e; mkdir -p '$MOODLE_ROOT_IN_CONTAINER/mod/assign/submission'; unzip -o '/tmp/$PLUGIN_ZIP_NAME' -d '$MOODLE_ROOT_IN_CONTAINER/mod/assign/submission'; php '$MOODLE_ROOT_IN_CONTAINER/admin/cli/upgrade.php' --non-interactive; php '$MOODLE_ROOT_IN_CONTAINER/admin/cli/purge_caches.php'"
echo "✅ Plugin installed."
# --- Step 2: Create and Configure a Custom Web Service ---
echo " -> Clearing cURL security settings..."
docker exec -u "$MOODLE_SYSTEM_USER" "$MOODLE_CONTAINER" php "$MOODLE_ROOT_IN_CONTAINER/admin/cli/cfg.php" --name=curlsecurityblockedhosts --set=""
docker exec -u "$MOODLE_SYSTEM_USER" "$MOODLE_CONTAINER" php "$MOODLE_ROOT_IN_CONTAINER/admin/cli/cfg.php" --name=curlsecurityallowedport --set=""
echo " -> Enabling Web Service File Uploads (System-wide)..."
docker exec -u "$MOODLE_SYSTEM_USER" "$MOODLE_CONTAINER" php "$MOODLE_ROOT_IN_CONTAINER/admin/cli/cfg.php" --name=webserviceuploaddisabled --set=0
# --- THIS IS THE FINAL FIX ---
# Enable username/password authentication for web service token generation
echo " -> Enabling password authentication for web services..."
docker exec -u "$MOODLE_SYSTEM_USER" "$MOODLE_CONTAINER" php "$MOODLE_ROOT_IN_CONTAINER/admin/cli/cfg.php" --name=enablewsauthpassword --set=1
echo "⚙️ Step 2: Creating and configuring a dedicated web service..."
docker exec -i "$DB_CONTAINER_NAME" mysql -u root -p"$DB_ROOT_PASSWORD" "$DB_NAME" <<EOF
-- Enable Web Services globally in Moodle's config
INSERT INTO mdl_config (name, value) VALUES ('enablewebservices', '1') ON DUPLICATE KEY UPDATE value = '1';
INSERT INTO mdl_config (name, value) VALUES ('webserviceprotocols', 'rest') ON DUPLICATE KEY UPDATE value = 'rest';
# --- End of addition --
-- Create our custom service, now with a requiredcapability set for robustness
INSERT IGNORE INTO mdl_external_services (name, shortname, enabled, requiredcapability, restrictedusers, uploadfiles, downloadfiles)
VALUES ('DTA Automation Service', '$CUSTOM_SERVICE_NAME', 1, '', 1, 1, 1); -- <-- The capability fix
-- Get the ID of our custom service
SET @serviceid = (SELECT id FROM mdl_external_services WHERE shortname = '$CUSTOM_SERVICE_NAME');
-- Add the required functions to OUR service
INSERT IGNORE INTO mdl_external_services_functions (externalserviceid, functionname) VALUES
(@serviceid, 'core_course_create_courses'),
(@serviceid, 'enrol_manual_enrol_users'),
(@serviceid, 'core_files_upload');
# -- *** THIS IS THE CRITICAL FIX ***
# -- Authorize the admin user to use this service.
SET @adminuserid = (SELECT id FROM mdl_user WHERE username = '$MOODLE_ADMIN_USER' AND deleted = 0 ORDER BY id ASC LIMIT 1);
INSERT IGNORE INTO mdl_external_services_users (externalserviceid, userid, iprestriction, validuntil, timecreated)
VALUES (@serviceid, @adminuserid, NULL, NULL, UNIX_TIMESTAMP());
EOF
echo "✅ Custom web service created and user authorized."
sleep 5
# --- THIS IS THE CRITICAL FIX ---
echo " -> Purging Moodle caches to apply service changes..."
docker exec -u "$MOODLE_SYSTEM_USER" "$MOODLE_CONTAINER" php "$MOODLE_ROOT_IN_CONTAINER/admin/cli/purge_caches.php"
echo "✅ Caches purged."
sleep 5
# --- Step 3: Get API Token (with Robust Checking) ---
echo "🔐 Step 3: Obtaining API Token...$MOODLE_ADMIN_USER..$MOODLE_ADMIN_PASS"
TOKEN_RESPONSE=$(curl --fail -sS -L -X POST "$MOODLE_URL/login/token.php" \
--data-urlencode "username=$MOODLE_ADMIN_USER" \
--data-urlencode "password=$MOODLE_ADMIN_PASS" \
--data-urlencode "service=$CUSTOM_SERVICE_NAME")
# Check for a Moodle error in the response FIRST
if echo "$TOKEN_RESPONSE" | jq -e 'if type=="object" and .error then true else false end' > /dev/null; then
echo "❌ Moodle API returned an error when requesting a token:"
echo "$TOKEN_RESPONSE" | jq .
exit 1
fi
TOKEN=$(echo "$TOKEN_RESPONSE" | jq -r '.token')
echo "✅ Successfully obtained API token. $TOKEN"
# --- Step 4: Create a Course ---
echo "📚 Step 4: Creating a new course..."
COURSE_SHORTNAME="DTA-Course-$(date +%s)"
COURSE_RESPONSE=$(curl --fail -sS -L -X POST "$MOODLE_URL/webservice/rest/server.php" \
-d "wstoken=$TOKEN" -d "wsfunction=core_course_create_courses" -d "moodlewsrestformat=json" \
-d "courses[0][fullname]=DTA Test Course" -d "courses[0][shortname]=$COURSE_SHORTNAME" -d "courses[0][categoryid]=1")
# --- THIS IS THE ROBUST ERROR CHECK ---
# First, check if the response is a Moodle exception object.
# The `jq -e` command sets an exit code, which works perfectly with `if`.
if echo "$COURSE_RESPONSE" | jq -e 'if type=="object" and .exception then true else false end' > /dev/null; then
echo "❌ Moodle API returned an exception during course creation:"
echo "$COURSE_RESPONSE" | jq . # Pretty-print the JSON error
exit 1
fi
# If not an exception, try to parse the ID. This handles both single-object and array responses.
NEW_COURSE_ID=$(echo "$COURSE_RESPONSE" | jq -r 'if type=="array" then .[0].id else .id end')
if [ -z "$NEW_COURSE_ID" ] || [ "$NEW_COURSE_ID" == "null" ]; then
echo "❌ Failed to parse a valid Course ID from the API response."
echo "Full API Response:"
echo "$COURSE_RESPONSE" | jq .
exit 1
fi
echo "✅ Successfully created course '$COURSE_SHORTNAME' with ID: $NEW_COURSE_ID"
# --- END OF ROBUST ERROR CHECK ---
# --- Step 5: Enroll Admin User into the New Course ---
echo "🧑‍🏫 Step 5: Enrolling you as a teacher in the new course..."
# First, get the User ID for your admin user
ADMIN_USER_ID=$(docker exec "$DB_CONTAINER_NAME" mysql -u root -p"$DB_ROOT_PASSWORD" "$DB_NAME" -se "SELECT id FROM mdl_user WHERE username='$MOODLE_ADMIN_USER';")
# The roleid for a Teacher is typically 3 (editingteacher) or 4 (teacher). We'll use 3.
TEACHER_ROLE_ID=3
# Add the enrol_manual_enrol_users function to our service
docker exec -i "$DB_CONTAINER_NAME" mysql -u root -p"$DB_ROOT_PASSWORD" "$DB_NAME" -e "INSERT IGNORE INTO mdl_external_services_functions (externalserviceid, functionname) VALUES ((SELECT id FROM mdl_external_services WHERE shortname = '$CUSTOM_SERVICE_NAME'), 'enrol_manual_enrol_users');"
# Purge cache to recognize the new function
docker exec -u "$MOODLE_SYSTEM_USER" "$MOODLE_CONTAINER" php "$MOODLE_ROOT_IN_CONTAINER/admin/cli/purge_caches.php" > /dev/null
ENROL_RESPONSE=$(curl --fail -sS -L -X POST "$MOODLE_URL/webservice/rest/server.php" \
-d "wstoken=$TOKEN" -d "wsfunction=enrol_manual_enrol_users" -d "moodlewsrestformat=json" \
-d "enrolments[0][roleid]=$TEACHER_ROLE_ID" \
-d "enrolments[0][userid]=$ADMIN_USER_ID" \
-d "enrolments[0][courseid]=$NEW_COURSE_ID")
# A successful response is null, so we check for an exception instead.
if echo "$ENROL_RESPONSE" | jq -e 'if type=="object" and .exception then true else false end' > /dev/null; then
echo "❌ Moodle API returned an exception during user enrollment:"
echo "$ENROL_RESPONSE" | jq .
exit 1
fi
echo "✅ Successfully enrolled you in the course."
# --- Step 5: Upload Configuration File for DTA Plugin ---
echo "📤 Step 5: Uploading config file for DTA plugin..."
FILE_UPLOAD_RESPONSE=$(curl --fail -sS -L -X POST "$MOODLE_URL/webservice/upload.php" \
-F "token=$TOKEN" -F "filearea=draft" -F "itemid=0" -F "file=@$DTA_CONFIG_FILE")
DTA_FILE_ITEMID=$(echo "$FILE_UPLOAD_RESPONSE" | jq -r 'if type=="array" then .[0].itemid else .itemid end')
if [ -z "$DTA_FILE_ITEMID" ] || [ "$DTA_FILE_ITEMID" == "null" ]; then
echo "❌ Failed to upload DTA config file. Response:"
echo "$FILE_UPLOAD_RESPONSE" | jq .
exit 1
fi
echo "✅ DTA config file uploaded. Received Item ID: $DTA_FILE_ITEMID"
# --- Step 6: Create Assignment via Custom PHP Script ---
echo "📝 Step 6: Creating assignment with detailed error logging..."
PHP_SCRIPT_PATH="/tmp/create_assignment_debug.php"
LOCAL_PHP_SCRIPT_PATH="/tmp/local_create_assignment_debug.php"
# --- This PHP script now has a try/catch block for detailed errors ---
cat > "$LOCAL_PHP_SCRIPT_PATH" <<EOF
<?php
define('CLI_SCRIPT', true);
require('/var/www/html/config.php');
require_once(\$CFG->dirroot.'/course/modlib.php');
global \$DB, \$USER;
// programmatically "log in" as the admin user
// This gives file_save_draft_area_files the user context it needs.
\$adminusername = '$MOODLE_ADMIN_USER';
\$adminuser = \$DB->get_record('user', ['username' => \$adminusername, 'deleted' => 0], '*', MUST_EXIST);
\core\session\manager::set_user(\$adminuser);
// The global $USER object is now populated.
if (count(\$argv) < 3) { exit("Error: Course ID and DTA Item ID are required.\n"); }
\$courseid = (int)\$argv[1];
\$dta_itemid = (int)\$argv[2];
\$course = \$DB->get_record('course', ['id' => \$courseid], '*', MUST_EXIST);
\$data = new stdClass();
// --- Core Module Details ---
\$data->course = \$courseid;
\$data->modulename = 'assign';
\$data->name = 'Fully Automated DTA Assignment';
\$data->intro = 'This assignment was created by the final automation script.';
\$data->introformat = FORMAT_HTML;
\$data->section = 1;
\$data->visible = 1;
// --- Submission Settings ---
\$data->assignsubmission_dta_enabled = 1;
\$data->assignsubmission_file_enabled = 1;
// --- THIS IS FIX #1: Correct property name for the DTA file manager ---
// This assumes the form element for your file manager is named 'package_filemanager'
// The final property name becomes assignsubmission_dta_package_filemanager
\$data->dta = 1; // Explicitly enable the dta group of settings
\$data->tests_draft_dta = \$dta_itemid;
\$data->assignsubmission_file_maxfiles = 20;
\$data->assignsubmission_onlinetext_enabled = 0;
\$data->submissiondrafts = 0; // Final submissions cannot be edited
\$data->requiresubmissionstatement = 0; // The direct fix for the error
\$data->requireallteammemberssubmit = 0;
\$data->teamsubmission = 0;
// --- Notification Settings ---
\$data->sendnotifications = 1;
\$data->sendlatenotifications = 0;
\$data->sendstudentnotifications = 1;
// --- Grading Settings ---
\$data->grade = 100; // Max grade
\$data->blindmarking = 0;
\$data->markingworkflow = 0;
\$data->markingallocation = 0;
\$data->attemptreopenmethod = 'none';
// --- Date Settings ---
\$data->allowsubmissionsfromdate = time();
\$data->duedate = time() + (7 * 24 * 60 * 60); // Due in 7 days
\$data->cutoffdate = 0; // No hard cutoff
\$data->gradingduedate = 0;
// --- Moodle Magic - these fields are necessary but we don't need to change them ---
\$data->module = \$DB->get_field('modules', 'id', ['name' => 'assign'], MUST_EXIST);
\$data->timemodified = time();
\$data->alwaysshowdescription = 0;
\$data->completionsubmit = 0;
// --- DEBUG: Print the exact data object we are sending ---
echo "DEBUG: Data object being sent to add_moduleinfo():\n";
print_r(\$data);
echo "\n";
try {
// Attempt to create the assignment
\$cm = add_moduleinfo(\$data, \$course);
if (!\$cm || !is_object(\$cm) || !\$cm->id) {
exit("Error: add_moduleinfo() returned a non-valid object but did not throw an exception.\n");
}
rebuild_course_cache(\$course->id, true);
echo "Successfully created assignment with Course Module ID: \$cm->id\n";
} catch (Exception \$e) {
// If anything goes wrong, catch the exception and print all details
echo "\n!!!!!! SCRIPT FAILED: An Exception was caught! !!!!!!\n\n";
echo "Error Type: " . get_class(\$e) . "\n";
echo "Error Message: " . \$e->getMessage() . "\n\n";
// Moodle's database exceptions often have extra useful debug info
if (!empty(\$e->debuginfo)) {
echo "Moodle Debug Info:\n" . \$e->debuginfo . "\n\n";
}
echo "Stack Trace:\n" . \$e->getTraceAsString() . "\n";
exit(1); // Exit with an error code
}
EOF
docker cp "$LOCAL_PHP_SCRIPT_PATH" "$MOODLE_CONTAINER:$PHP_SCRIPT_PATH"
docker exec -u "$MOODLE_SYSTEM_USER" "$MOODLE_CONTAINER" php "$PHP_SCRIPT_PATH" "$NEW_COURSE_ID" "$DTA_FILE_ITEMID"
docker exec "$MOODLE_CONTAINER" rm "$PHP_SCRIPT_PATH"
rm "$LOCAL_PHP_SCRIPT_PATH"
echo "✅ Assignment created successfully using the custom PHP script."
echo "🎉🎉 Moodle automation complete. The environment is fully configured. 🎉🎉"
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment